/* * This file is part of the OpenJML project. * Author: David R. Cok */ package org.jmlspecs.openjml.esc; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.Set; import javax.lang.model.element.ElementVisitor; import javax.lang.model.type.TypeKind; import javax.tools.JavaFileObject; import org.jmlspecs.annotation.NonNull; import org.jmlspecs.annotation.Nullable; import org.jmlspecs.openjml.*; import org.jmlspecs.openjml.JmlSpecs.FieldSpecs; import org.jmlspecs.openjml.JmlSpecs.TypeSpecs; import org.jmlspecs.openjml.JmlTree.JmlBBArrayAccess; import org.jmlspecs.openjml.JmlTree.JmlBinary; import org.jmlspecs.openjml.JmlTree.JmlBlock; import org.jmlspecs.openjml.JmlTree.JmlChoose; import org.jmlspecs.openjml.JmlTree.JmlClassDecl; import org.jmlspecs.openjml.JmlTree.JmlCompilationUnit; import org.jmlspecs.openjml.JmlTree.JmlMethodSig; import org.jmlspecs.openjml.JmlTree.JmlDoWhileLoop; import org.jmlspecs.openjml.JmlTree.JmlEnhancedForLoop; import org.jmlspecs.openjml.JmlTree.JmlForLoop; import org.jmlspecs.openjml.JmlTree.JmlGroupName; import org.jmlspecs.openjml.JmlTree.JmlImport; import org.jmlspecs.openjml.JmlTree.JmlLblExpression; import org.jmlspecs.openjml.JmlTree.JmlMethodClause; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseCallable; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseConditional; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseDecl; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseExpr; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseGroup; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignals; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignalsOnly; import org.jmlspecs.openjml.JmlTree.JmlMethodClauseStoreRef; import org.jmlspecs.openjml.JmlTree.JmlMethodDecl; import org.jmlspecs.openjml.JmlTree.JmlMethodInvocation; import org.jmlspecs.openjml.JmlTree.JmlMethodSpecs; import org.jmlspecs.openjml.JmlTree.JmlModelProgramStatement; import org.jmlspecs.openjml.JmlTree.JmlPrimitiveTypeTree; import org.jmlspecs.openjml.JmlTree.JmlQuantifiedExpr; import org.jmlspecs.openjml.JmlTree.JmlSetComprehension; import org.jmlspecs.openjml.JmlTree.JmlSingleton; import org.jmlspecs.openjml.JmlTree.JmlSpecificationCase; import org.jmlspecs.openjml.JmlTree.JmlStatement; import org.jmlspecs.openjml.JmlTree.JmlStatementDecls; import org.jmlspecs.openjml.JmlTree.JmlStatementExpr; import org.jmlspecs.openjml.JmlTree.JmlStatementHavoc; import org.jmlspecs.openjml.JmlTree.JmlStatementLoop; import org.jmlspecs.openjml.JmlTree.JmlStatementSpec; import org.jmlspecs.openjml.JmlTree.JmlStoreRefArrayRange; import org.jmlspecs.openjml.JmlTree.JmlStoreRefKeyword; import org.jmlspecs.openjml.JmlTree.JmlStoreRefListExpression; import org.jmlspecs.openjml.JmlTree.JmlTypeClause; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConditional; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConstraint; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseDecl; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseExpr; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseIn; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseInitializer; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMaps; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMonitorsFor; import org.jmlspecs.openjml.JmlTree.JmlTypeClauseRepresents; import org.jmlspecs.openjml.JmlTree.JmlVariableDecl; import org.jmlspecs.openjml.JmlTree.JmlWhileLoop; import org.jmlspecs.openjml.JmlTree.Maker; import org.jmlspecs.openjml.Utils.JmlNotImplementedException; import org.jmlspecs.openjml.ext.Arithmetic; import com.sun.source.tree.*; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Scope; 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.VarSymbol; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Enter; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.comp.JmlAttr; import com.sun.tools.javac.comp.JmlEnter; import com.sun.tools.javac.jvm.ClassReader; import com.sun.tools.javac.parser.ExpressionExtension; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssert; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCContinue; import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCErroneous; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCInstanceOf; import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCSkip; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCSynchronized; import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.tree.JCTree.LetExpr; import com.sun.tools.javac.tree.JCTree.TypeBoundKind; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.Log.WriterKind; /** This class translates an attributed Java+JML AST, creating a new * Java-compatible AST that includes assertions to check for all the various * Java and JML conditions that need checking. * <P> * The resulting AST is an (almost) complete copy - it does not share any * mutable structure with the original AST, so the original AST can be reused; * it represents each identifier in a separate JCIdent, so that a succeeding * Single-assignment operation can change identifier names in place. * <UL> * <LI>If the field 'fullTranslation' is true, then all AST nodes, even * non-mutable nodes, such as JCLiteral, are duplicated. The copied AST may * still share non-AST objects such as Name and Symbol objects, and references * to MethodSpecs, TypeSpecs, and FieldSpecs. * <LI>If the field 'fullTranslation' is false, then, for efficiency, some node * classes, considered to be non-mutable, are still shared with the original AST, * such as JCLiteral, JCAnnotation, JCModifiers. * <P> * There are three modes of translation: * <UL> * <LI>pureCopy=true: This makes an (almost) pure copy of the AST, without * converting or adding any nodes. Note that any translation mode may use * pureCopy for a sub-tree. Also note that some nodes include references to * other nodes in the tree (e.g., a class declaration includes a reference to * its top-level compilation unit); these are appropriately translated. * The 'almost' is because * (a) the result is still affected by the 'fullTranslation' option, and (b) * some nodes are expanded - type nodes are expanded to be fully-qualified types * and class field references will have 'this' or the fully-qualified typename * prepended. * * <LI>esc=true,rac=false,pureCopy=false: This inserts all JML semantics as new * Java constructs and JML assert and assume statements, * retaining the Java control flow statements. The result is a Java AST * with some JML features but is not executable. * * <LI>rac=true,esc=false,pureCopy=false: This inserts all executable JML checks * as calls to an assertionFailure method - which can dynamically choose to * print out warning messages, throw exceptions, or execute a Java assert. * The translated AST is a transformation of the original program, but is * functionally equivalent to it, aside from the additional checks. * * <LI>esc=true,rac=true,pureCopy=false: unsupported. * <LI>esc=false,rac=false,pureCopy=false: unsupported. * </UL> * <P> * With either rac or esc on, the translated AST uses only a subset of Java * functionality. All JML features are translated into assertions and Java statements. * <P> * Within both the rac and esc modes, subtrees may be translated with * 'translatingJML' true - this should not be set by the caller, but is used to * translate Java constructs in JML expressions differently than Java constructs * in Java code. // * <UL> -- FIXME - fix all of the following // * <LI>Java expressions: // * </UL> // * <LI>binary operations - arithmetic, bit, logical all allowed in Java statements and JML assertions; // * instanceof is allowed in Java, translated into a type relationship in JML // * <LI>unary operations - minus and negation allowed; pre- and post-increment // * and decrement converted to separate operations and assignment // * <LI>assignment - retained // * <LI>assign-op - separated into operation and assignment // * <LI>type cast - TBD // * <LI>field selection - TBD // * <LI>array index - retained, but uses JmlBBArrayAccess nodes instead of JCArrayAccess // * <LI> // * <LI>object allocation - TBD // * <LI>array allocation - TBD // * <LI>anonymous class expression - TBD // * <LI>...TBD // * </UL> // * <LI>Java statements: // * <UL> // * <LI>if, switch, try statements are retained // * <LI>for, while, do, foreach statements are retained but may be transformed // * into other loop types to accommodate inserting loop specifications // * <LI>variable declarations - TBD // * <LI>local class declarations - TBD // * <LI>method declarations, class declarations, import declarations - all retained // * </UL> // * <LI> JML expressions: // * <UL> // * <LI> binary logical operations - converted to Java equivalents // * <LI> subtype operation - TBD // * <LI> ... TBD // * </UL> // * <LI> JML statements and features: // * <UL> // * <LI> assert, assume statements - is esc mode, these are retained as JML statements // * in rac mode, they are converted to RAC checks // * <LI> method clauses: requires, ensures, signals - converted to assertions // * </UL> * * */ // DESIGN NOTE: This AST visitor operates in two modes - when translating Java // and when translating JML. These are both implemented in the one tree visitor, // controlled by the class field 'translatingJML'. I considered having two // scanners, one for each mode. However, there was then a lot of code // duplication or similarity, with the similar code being separated into two // classes. Also, calling the correct translation function proved to be more // error prone than in the design with just one scanner class. For better or // worse, the one-class design is what is implemented here. // DESIGN NOTE: We can't simply extend a tree copier because (a) many nodes are // transformed into nodes of a different class, and (b) many nodes produce // multiple output nodes - e.g. statements are transformed into a series of // statements. public class JmlAssertionAdder extends JmlTreeScanner { // Parameters of this instance of JmlAssertionAdder /** If true then every part of every AST is copied; if false then items * expected to be immutable such as JCLiteral, qualified ids (in import * statements, static type designators), JCAnnotation are not duplicated. * Non-AST objects * such as Type, Token or JmlToken values are not duplicated in any case. */ public boolean fullTranslation = true; // NOTE: We support !esc || !rac but not esc && rac. //@ invariant !esc || !rac; /** True if we are translating for static checks */ public boolean esc ; /** True if we are translating for RAC */ public boolean rac ; /** If true, we are making a pure copy (!esc && !rac)*/ public boolean pureCopy; /** If true, then error messages in generated RAC code include source * code snippets with the customary textual ^ pointers to error locations. * This adds bulk to the RAC-ed program, though I've not measured whether * it is significant. The field is initialized from a user option and not meant * to be set externally. */ protected boolean showRacSource; /** If true, then in the RAC translation, assume statements and assumptions * implied by JML are checked as if they were assert statements. * The field is initialized from a user option and not meant to be set * externally. */ protected boolean racCheckAssumeStatements; /** If true, then explicit checks are included even when the Java * language would catch the error itself (e.g., OpenJML will check for a * null reference in advance of a dereference and Java throwing a * NullPointerException). This should always be true for esc, but only * true for rac if the appropriate option is set. */ public boolean javaChecks; // Constant items set in the constructor /** The compilation context */ final protected Context context; /** Cached value of the Log tool */ final public Log log; /** Cached value of the specs database */ final protected JmlSpecs specs; /** Cached value of JmlTypes */ final public JmlTypes jmltypes; /** Cached value of the AST node factory */ final public JmlTree.Maker M; /** Cached value of the names table */ final public Names names; /** Cached value of the symbol table */ final public Symtab syms; /** Cached value of the Types tool */ final public Types types; /** Cached value of the Utils tool */ final public Utils utils; /** Cached value of the Nowarns object */ final protected Nowarns nowarns; /** Cached value of the Attribute tool */ final protected JmlAttr attr; /** The JmlTreeUtils object, holding a bunch of tree-making utilities */ final public JmlTreeUtils treeutils; /** The tool to find class symbols */ final protected ClassReader reader; /** The symbol for the runtime Utils class */ final protected ClassSymbol utilsClass; /** The Name used for the result of a method */ final protected Name resultName; /** The symbol for the variable that holds the result of a method */ protected Symbol resultSym = null; /** An expression to be used for \result when translating postconditions; * the expression should always be copied afresh for each instantiation */ protected JCExpression resultExpr = null; /** The Name used for exceptions thrown in the body of a method */ final protected Name exceptionName; /** The symbol for the variable that tracks exceptions in the body of a method */ protected Symbol exceptionSym = null; /** The symbol for the variable that holds allocation ids to distinguish dynamically allocated objects */ protected Symbol allocSym = null; /** The symbol for the variable that says whether an object is allocated */ protected Symbol isAllocSym = null; /** A counter used to make distinct ids for newly allocated Objects */ protected int allocCounter = 0; /** Exception Symbols used for various methods */ protected Map<JCMethodDecl,Symbol> exceptionSymbols = new HashMap<JCMethodDecl,Symbol>(); /** The Name used for catching exceptions thrown by called methods */ final protected Name exceptionNameCall; /** The Name used for holding the location at which the final return or throw statement occurs */ final protected Name terminationName; /** The symbol to go with terminationName. */ protected Symbol terminationSym = null; /** Termination Symbols used for various methods */ protected Map<JCMethodDecl,Symbol> terminationSymbols = new HashMap<JCMethodDecl,Symbol>(); // Fields used and modified during translation // These should only be modified by visit methods /** The AST being processed when in a sub-tree of a method declaration */ protected JmlMethodDecl methodDecl = null; /** The parent class of the method being converted, for use while the * declarations of the class are being walked, and while a method is * being translated stand-alone (without having been reached by walking * the tree from above). */ protected JmlClassDecl classDecl = null; /** The Ident to use when translating this - starts as the this for the * receiver object, but can change as methods or constructors are called. */ protected JCIdent currentThisId; protected JCExpression currentThisExpr; protected Symbol enclosingMethod; protected Symbol enclosingClass; /** The mode to use to model arithmetic operations - only null until initialized */ protected IArithmeticMode currentArithmeticMode = null; /** Depth of nesting of applyHelper calls */ protected int applyNesting; /** The counter used to make uniquely named variables for preconditions, * unique within a method body. */ int precount = 0; /** The counter used to make uniquely named variables for assertions, * unique within a method body. */ protected int assertCount = 0; /** A counter that ensures unique variable names (within a method body). */ protected int count = 0; /** A map from formal parameter to actual argument, used when translating * methods called within a method body; also used to map formals in inherited * method specs to actual arguments or to the formals in the base method. */ protected Map<Object,JCExpression> paramActuals; /** A map from formals to a declaration of a variable that holds the formal's * value at method body entrance (for use by postconditions). */ protected Map<Symbol,JCVariableDecl> preparams = new HashMap<Symbol,JCVariableDecl>(); /** A map from specification case to a JCIdent that is a variable holding * the value of the precondition for that specification case. */ protected Map<JmlSpecificationCase,JCIdent> preconditions = new HashMap<JmlSpecificationCase,JCIdent>(); /** A map from old nodes to new ones, for use when there are node references * (rather than copies of trees) within an AST. In particular used to * set the target fields for break and continue statements. */ protected java.util.Map<JCTree,JCTree> treeMap = new HashMap<JCTree,JCTree>(); /** A List used to accumulate translated definitions of a class, for cases * where new declarations are needed. */ protected ListBuffer<JCTree> classDefs = null; /** A list to collect statements as they are being generated. */ protected ListBuffer<JCStatement> currentStatements; /** A list aliased with the place to put computations of \pre expressions. */ protected ListBuffer<JCStatement> oldStatements; /** The prelude statements of the current method */ protected ListBuffer<JCStatement> initialStatements; /** A stack of 'currentStatements' . The current value of 'currentStatements' * is NOT on this stack. */ protected LinkedList<ListBuffer<JCStatement>> statementStack = new LinkedList<ListBuffer<JCStatement>>(); /** A stack of labeled statements that might be targets of continue statements */ java.util.Stack<JCLabeledStatement> continueStack = new java.util.Stack<JCLabeledStatement>(); /** Stack of the synthetic index (trip count) variables for loops enclosing * the code under current consideration. 0th element is the currently innermost scope */ protected java.util.List<JCVariableDecl> indexStack = new LinkedList<JCVariableDecl>(); /** true when translating JML constructs, false when translating Java constructs. * This is set and manipulated by the visitor methods */ public boolean translatingJML = false; public boolean splitExpressions = true; public boolean convertingAssignable = false; public boolean assumingPureMethod = false; /** Contains an expression that is used as a guard in determining whether expressions * are well-defined. For example, suppose we are translating the expression * a != null && a[i] == 0. Then condition is 'true' when a!=null is translated. * But when a[i] is translated, 'condition' will be a != null. The well-definedness * check for a[i] will then be (a != null) ==> (a != null && i >= 0 && i < a.length). * So the full expression is well-defined only if that implication can be proved given * other pre-conditions. */ public JCExpression condition; // FIXME - dcoument protected java.util.List<JmlStatementExpr> wellDefinedConditions = new java.util.LinkedList<JmlStatementExpr>(); /** Set to true when we are translating a normal or exceptional postcondition. It is used * to be sure the correct scope is used when method parameters are used in the postcondition. * If a method parameter is used in a postcondition it is evaluated in the pre-state since * any changes to the parameter within the body of the method are discarded upon exit and * are invisible outside the method (i.e. in the postcondition). */ protected boolean isPostcondition; /** Used to note the environment (i.e., \old label) under which we are currently * evaluating; null indicates the current state; an empty string indicates * the pre-state, otherwise it is the JCIdent of the label in the \old statement */ @Nullable protected JCIdent oldenv; /** The \old label to use for the pre-state */ protected JCIdent preLabel; /** Used to hold the result of non-expression AST nodes */ protected JCTree result; /** Used to hold the result of expression AST nodes, so equal to 'result' * when 'result' is a JCExpression. */ protected JCExpression eresult; protected JCBlock axiomBlock = null; /** Assertions that can be changed to be feasibility checks */ public Map<Symbol,java.util.List<JCTree.JCParens>> assumptionChecks = new HashMap<Symbol,java.util.List<JCTree.JCParens>>(); public Map<Symbol,java.util.List<JmlTree.JmlStatementExpr>> assumptionCheckStats = new HashMap<Symbol,java.util.List<JmlTree.JmlStatementExpr>>(); // FIXME - review which of these bimaps is actually needed /** A bi-map used to record the mapping between original and rewritten nodes, for reporting expression values */ public BiMap<JCTree, JCTree> exprBiMap = new BiMap<JCTree, JCTree>(); /** A bi-map used to record statement mappings for reporting counterexample paths */ public BiMap<JCTree,JCTree> pathMap = new BiMap<JCTree,JCTree>(); /** A bi-map used to record the mapping between original and rewritten method ASTs */ public BiMap<JmlMethodDecl, JmlMethodDecl> methodBiMap = new BiMap<JmlMethodDecl, JmlMethodDecl>(); /** A bi-map used to record the mapping between original and rewritten class ASTs */ public BiMap<JmlClassDecl, JmlClassDecl> classBiMap = new BiMap<JmlClassDecl, JmlClassDecl>(); /** A map from Class Symbols to an ident containing the this symbol for that class */ public Map<Symbol,JCIdent> thisIds = new HashMap<Symbol,JCIdent>(); public int assumeCheckCount = 0; public final static String assumeCheckVar = "__JML_AssumeCheck_"; public VarSymbol assumeCheckSym; public Map<JmlMethodDecl,java.util.List<JmlStatementExpr>> assumeChecks = new HashMap<JmlMethodDecl,java.util.List<JmlStatementExpr>>(); public int heapCount = 0; public VarSymbol heapSym = null; public Name heapVarName; /** (Public API) Creates an object to do the rewriting and assertion insertion. This object * can be reused to translate different method bodies, so long as the arguments * to this constructor remain appropriate. May not have both esc and rac true; * if both are false, the mode is implicitly pureCopy. * @param context the compilation context to be used * @param esc true if the resulting AST is to be used for ESC, otherwise false * @param fac true if the resulting AST is to be used for RAC, otherwise false */ public JmlAssertionAdder(Context context, boolean esc, boolean rac) { this.context = context; this.esc = esc; this.rac = rac; this.log = Log.instance(context); this.M = JmlTree.Maker.instance(context); this.names = Names.instance(context); this.nowarns = Nowarns.instance(context); this.syms = Symtab.instance(context); this.types = Types.instance(context); this.utils = Utils.instance(context); this.specs = JmlSpecs.instance(context); this.jmltypes = JmlTypes.instance(context); this.treeutils = JmlTreeUtils.instance(context); this.attr = JmlAttr.instance(context); this.resultName = names.fromString(Strings.resultVarString); this.exceptionName = names.fromString(Strings.exceptionVarString); this.exceptionNameCall = names.fromString(Strings.exceptionCallVarString); this.terminationName = names.fromString(Strings.terminationVarString); this.reader = ClassReader.instance(context); this.reader.init(syms); this.utilsClass = reader.enterClass(names.fromString(Strings.runtimeUtilsFQName)); this.preLabel = treeutils.makeIdent(Position.NOPOS,Strings.empty,syms.intType); // Type does not matter this.checkAccessEnabled = JmlOption.isOption(context,JmlOption.CHECK_ACCESSIBLE); initialize(); } /** (Public API) Reinitializes the object to start a new class or compilation unit or method */ public void initialize() { this.showRacSource = JmlOption.isOption(context,JmlOption.RAC_SHOW_SOURCE); this.racCheckAssumeStatements = JmlOption.isOption(context,JmlOption.RAC_CHECK_ASSUMPTIONS); this.javaChecks = esc || (rac && JmlOption.isOption(context,JmlOption.RAC_JAVA_CHECKS)); this.count = 0; this.assertCount = 0; this.precount = 0; this.preparams.clear(); this.preconditions.clear(); //this.labels.clear(); this.pureCopy = !(esc||rac); this.treeMap.clear(); this.oldenv = null; this.heapCount = 0; this.heapVarName = names.fromString("_heap__"); // FIXME - cf. BasicBlocker2 this.applyNesting = 0; racMessages.clear(); escMessages.clear(); assumptionChecks.clear(); assumptionCheckStats.clear(); this.useMethodAxioms = !JmlOption.isOption(context,JmlOption.MINIMIZE_QUANTIFICATIONS); } public void initialize2(long flags) { MethodSymbol msym; if (methodDecl != null) { msym = methodDecl.sym; } else { // We are in an initializer block // We need a method symbol to be the owner of declarations // (otherwise they will have the class as owner and be thought to // be fields) msym = new MethodSymbol( flags, classDecl.name, null, classDecl.sym); methodDecl = //M.MethodDef(msym, null,null); new JmlMethodDecl( M.Modifiers(flags, M.Annotations(List.<com.sun.tools.javac.code.Attribute.Compound>nil())), classDecl.name, null, null, null, null, null, null, //body, null, msym); } if (!pureCopy) { JCVariableDecl d = treeutils.makeVarDef(syms.exceptionType,exceptionName,msym, treeutils.makeNullLiteral(methodDecl.pos)); exceptionSym = d.sym; exceptionSymbols.put(methodDecl,exceptionSym); currentStatements.add(d); } if (!pureCopy) { JCVariableDecl d = treeutils.makeVarDef(syms.intType,terminationName,methodDecl.sym, treeutils.makeIntLiteral(methodDecl.pos,0)); terminationSym = d.sym; terminationSymbols.put(methodDecl, terminationSym); currentStatements.add(d); } if (esc) { Name name = names.fromString(assumeCheckVar); JCVariableDecl d = treeutils.makeVarDef(syms.intType, name, methodDecl.sym, Position.NOPOS); // NOPOS so the name is not mangled assumeCheckSym = d.sym; d.sym.owner = null; currentStatements.add(d); } exprBiMap.put(treeutils.nullLit,treeutils.nullLit); } // FIXME - this comment needs review and correction /** Creates the overall framework for the block: * <PRE> preconditions try { method body } finally { postconditions } </PRE> * <P> * The converted method body is a new block with the following characteristics: * <UL> * <LI>control flow statements are still present: if-then-else, switch, try-catch-finally blocks * <LI>expressions in Java code are decomposed into individual operations, with temporaries. This is so that * (a) it is easy to add any assertions prior to a specific operation * (b) it is straightforward to handle any operation with side-effects, even in the context of * short-circuit operations * <LI>assertions are added in for any checks that are desired (see the list below) * <LI>specification expressions are not decomposed into individual operations, since all the * sub-expressions are supposed to be pure; however, additional assertions are added before any * specification expression to check that the specification expression is well-defined - TODO * <LI>conditional and short-circuit expressions (in Java code) are converted into if-then-else statements; * again, this is to simplify handling of side-effects in sub-expressions - TODO for short-circuit * <LI>all identifiers are made unique by combining the name with location information; but no * conversions for single-assignment are performed - TODO * </UL> * <P> * These operations are converted: * <UL> * <LI>JML equivalence to boolean = * <LI>JML inequivalence to boolean != * <LI>JML reverse implies (p <== q) to (p || !q) * <LI>Java assignments with operations are decomposed into the operation and the assignment * </UL> * <LI>These operations are retained: * <UL> * <LI>assignment * <LI>integer and floating +, -, *, /, % * <LI>== and != * <LI>comparison operations (integer and floating) * <LI>bit and or xor * * </UL> * <LI>TODO: mod, integer division, shift operations, bit operations, JML implies, JML subtype, * instanceof, short-circuit boolean operations * <LI>TODO: handle method calls * <LI>TODO: handle method calls in specifications * <LI>TODO: new Object, new Array expressions * <LI>TODO: quantifier and set comprehension expressions * <LI>TODO: \fresh operation * <LI>TODO: \nonnullelements * </UL> * <P> * These assumptions and checks are added in: * <UL> * <LI>assume the method preconditions, including null method parameters * <LI>assume any class invariants, including null field declarations * <LI>assert the method postconditions * <LI>assume any explicit assumption * <LI>assert any explicit assertion * <LI>assert the method exceptional postconditions - TODO * <LI>check for null-dereference on each field access * <LI>check for null-dereference on each array access - TODO * <LI>check for index out of bounds on each array access - TODO * <LI>check for assignment of null to any non-null variable or field * <LI>check for out of range arithmetic operations - TODO * </UL> * * @return JCBlock with all assumptions, assertions, added declarations */ /** (Public API) Returns a new JCBlock representing the rewritten body of the given method declaration. */ public JCBlock convertMethodBody(JmlMethodDecl pmethodDecl, JmlClassDecl pclassDecl) { initialize(); return convertMethodBodyNoInit(pmethodDecl,pclassDecl); } Name defaultOldLabel = null; Map<TypeSymbol,Type> typevarMapping; protected boolean assumingPostConditions; /** Internal method to do the method body conversion */ protected JCBlock convertMethodBodyNoInit(JmlMethodDecl pmethodDecl, JmlClassDecl pclassDecl) { Main.instance(context).pushOptions(pmethodDecl.mods); int prevAssumeCheckCount = assumeCheckCount; JmlMethodDecl prev = this.methodDecl; JmlClassDecl prevClass = this.classDecl; JCIdent savedThisId = this.currentThisId; JCExpression savedThisExpr = this.currentThisExpr; Symbol savedExceptionSym = this.exceptionSym; Symbol savedEnclosingMethod = this.enclosingMethod; Symbol savedEnclosingClass = this.enclosingClass; Symbol savedResultSym = this.resultSym; Symbol savedTerminationSym = this.terminationSym; IArithmeticMode savedArithmeticMode = this.currentArithmeticMode; ListBuffer<JCStatement> prevStats = initialStatements; ListBuffer<JCStatement> savedOldStatements = oldStatements; JavaFileObject prevSource = log.useSource(pmethodDecl.source()); Map<Object,JCExpression> savedParamActuals = paramActuals; java.util.List<Symbol> savedCompletedInvariants = this.completedInvariants; Set<Symbol> savedInProcessInvariants = this.inProcessInvariants; Name savedOldLabel = defaultOldLabel; boolean isModel = isModel(pmethodDecl.sym); currentArithmeticMode = Arithmetic.Math.instance(context).defaultArithmeticMode(pmethodDecl.sym,false); if (!isModel) addAxioms(-1,null); assumingPostConditions = true; typevarMapping = typemapping(pclassDecl.type, null, null); try { enclosingMethod = pmethodDecl.sym; enclosingClass = pmethodDecl.sym.owner; if (isModel && (pmethodDecl.mods.flags & Flags.SYNTHETIC) != 0) { return convertMethodBodyNoInitModel(pmethodDecl, pclassDecl); } assumeCheckCount = 0; this.methodDecl = pmethodDecl; this.classDecl = pclassDecl != null ? pclassDecl : utils.getOwner(methodDecl) ; this.initialStatements = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> outerFinalizeStats = new ListBuffer<JCStatement>(); boolean isConstructor = methodDecl.sym.isConstructor(); oldStatements = initialStatements; currentStatements = initialStatements; { // Type relationships for generic type parameters and their bounds for (JCTypeParameter tp: classDecl.getTypeParameters()) { for (JCExpression bound: tp.getBounds()) { JCIdent id = M.Ident(tp.name); id.sym = tp.type.tsym; id.type = tp.type; JCExpression t1 = M.at(tp.pos).JmlMethodInvocation(JmlTokenKind.BSTYPELC, id); t1.type = jmltypes.TYPE; JCExpression t2 = M.at(bound.pos).JmlMethodInvocation(JmlTokenKind.BSTYPELC, convertCopy(bound)); t2.type = jmltypes.TYPE; JCExpression e = M.at(tp).JmlBinary(JmlTokenKind.SUBTYPE_OF,t1,t2); e.type = syms.booleanType; e = convertJML(e); addAssume(bound,Label.IMPLICIT_ASSUME,e); } } } JCLabeledStatement mark = M.Labelled(preLabel.name, M.Block(0L, List.<JCStatement>nil())); markLocation(preLabel.name,initialStatements,mark); if (isConstructor) { // JCVariableDecl d = treeutils.makeVarDef(classDecl.type,resultName,methodDecl.sym, // treeutils.makeZeroEquivalentLit(methodDecl.pos,classDecl.type)); JCVariableDecl d = treeutils.makeVarDef(classDecl.type,resultName,methodDecl.sym, currentThisExpr); resultSym = d.sym; initialStatements.add(d); } else if (methodDecl.restype.type.getTag() != TypeTag.VOID) { // The compiler complains that the result variable might not be // initialized on every path, even in programs in which it appears // obvious that it is. So we initialize it here to a null-like value. JCVariableDecl d = treeutils.makeVarDef(methodDecl.restype.type,resultName,methodDecl.sym, treeutils.makeZeroEquivalentLit(methodDecl.pos,methodDecl.restype.type)); resultSym = d.sym; initialStatements.add(d); } else { resultSym = null; } resultExpr = resultSym == null ? null : treeutils.makeIdent(methodDecl.pos,resultSym); if (esc && heapSym == null) { JCVariableDecl d = treeutils.makeStaticVarDef(syms.intType,heapVarName,null, treeutils.makeIntLiteral(0, 0)); heapSym = d.sym; initialStatements.add(d); } initialize2(0L); if (allocSym == null) { allocSym = treeutils.makeVarSymbol(0, names.fromString(Strings.allocName), syms.intType, classDecl.pos); allocSym.owner = classDecl.sym; isAllocSym = treeutils.makeVarSymbol(0, names.fromString(Strings.isAllocName), syms.booleanType, classDecl.pos);; isAllocSym.owner = classDecl.sym; if (esc) { // THIS is an id that is a proxy for the this object on which a method is called; // we need to distinguish it from uses of 'this' in the text // FIXME - should make this NonNull // VarSymbol THISSym = treeutils.makeVarSymbol(Flags.STATIC,names.fromString(Strings.thisName),classDecl.sym.type, Position.NOPOS); // THISSym.owner = classDecl.sym; // this.currentThisId = treeutils.makeIdent(classDecl.pos,THISSym); // this.thisIds.put(classDecl.sym, this.currentThisId); // exprBiMap.put(this.currentThisId, this.currentThisId); this.currentThisId = makeThisId(classDecl.pos,classDecl.sym); this.currentThisExpr = this.currentThisId; } else { // rac // For RAC we use the actual 'this' FIXME - not sure about this design - perhaps just need to be cautious about what is translated and what is not this.currentThisId = treeutils.makeIdent(classDecl.pos, classDecl.thisSymbol); this.currentThisExpr = this.currentThisId; this.thisIds.put(classDecl.sym, currentThisId); } } if (esc && (isConstructor || !utils.isJMLStatic(methodDecl.sym))) { currentStatements = initialStatements; JCExpression e = treeutils.makeNeqObject(methodDecl.pos,currentThisExpr,treeutils.nullLit); addAssume(methodDecl,Label.IMPLICIT_ASSUME,e); addAssume(classDecl,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(classDecl,currentThisExpr,classDecl.type)); JCExpression fa = M.at(methodDecl.pos).Select(currentThisExpr, allocSym); fa = treeutils.makeBinary(methodDecl,JCTree.Tag.EQ, fa, treeutils.makeIntLiteral(methodDecl,0)); addStat(treeutils.makeAssume(methodDecl, Label.IMPLICIT_ASSUME, fa )); } boolean callingSuper = false; boolean callingThis = false; Iterator<JCStatement> iter = null; if (methodDecl.body != null) { iter = methodDecl.body.stats.iterator(); if (iter.hasNext()) { JCStatement st = methodDecl.body.stats.get(0); if (st instanceof JCExpressionStatement && ((JCExpressionStatement)st).expr instanceof JCMethodInvocation) { JCMethodInvocation mi = (JCMethodInvocation)((JCExpressionStatement)st).expr; if (mi.meth instanceof JCIdent) { JCIdent id = (JCIdent)mi.meth; if (id.name.equals(names._this)) callingThis = true; else if (id.name.equals(names._super)) callingSuper = true; } } } } if (isConstructor && rac) { pushBlock(); if (callingThis || callingSuper) { convertCopy(iter.next()); } JCBlock bl = popBlock(0,methodDecl); if (!pureCopy) addPreConditions(initialStatements); // FIXME - need to fix RAC So it can check preconditions etc. pushBlock(); JCBlock newMainBody = null; try { while (iter.hasNext()) { convert(iter.next()); } } finally { newMainBody = popBlock(0,methodDecl.body == null ? methodDecl: methodDecl.body); } // The outerTryStatement just has a finally clause in which the // postconditions and exceptional postconditions are checked. JCCatch c; { // global catch block JCVariableDecl ex = treeutils.makeVarDef(syms.exceptionType,names.fromString("_JML__ex"),methodDecl.sym,methodDecl.pos); pushBlock(); addStat(treeutils.makeAssignStat(methodDecl.pos, treeutils.makeIdent(methodDecl.pos,exceptionSym), treeutils.makeIdent(methodDecl.pos, ex.sym))); addStat(M.at(methodDecl.pos).Throw(treeutils.makeIdent(methodDecl.pos, ex.sym))); JCBlock bbl = popBlock(0,methodDecl); c = M.at(methodDecl.pos).Catch(ex, bbl); } if (!pureCopy) addPostConditions(outerFinalizeStats); JCTry outerTryStatement = M.at(methodDecl).Try( newMainBody, esc ? List.<JCCatch>nil() : List.<JCCatch>of(c), M.Block(0, outerFinalizeStats.toList())); initialStatements.add(outerTryStatement); bl.stats = bl.stats.appendList(initialStatements); return M.at(methodDecl.body).Block(methodDecl.body.flags,bl.stats); } // The form of the translated method is this: // assume invariants and preconditions, assigning precondition // values to designated identifiers [initialStatements] // try { // translation of all the method body statements [currentStatements, after the pushBlock] // } finally { // assert all the postconditions, suitably guarded by the // values of the preconditions in each specification case // (which is why they have to be declared before the try // statement) [outerFinalizeStatements] // } // Values of the result, any exception active, and the termination // position (location of last return or throw statement) are tracked // We create this first, so that it is the first one in the list. // We'll add the block into the right spot later. // Other checks will be created during addPrePostConditions ListBuffer<JCStatement> check = pushBlock(); // FIXME - should we have a try block? //if (pclassDecl.name.toString().equals("ArrayFieldVector") && pmethodDecl.name.toString().equals("hashCode")) Utils.stop(); if (!pureCopy) { addPreConditions(initialStatements); pushBlock(); addAssumeCheck(methodDecl,currentStatements,Strings.preconditionAssumeCheckDescription); // FIXME - use a smaller highlight range than the whole method - perhaps the specs? JCStatement preconditionAssumeCheck = popBlock(0,methodDecl); addStat(initialStatements,preconditionAssumeCheck); } addStat( comment(methodDecl,"Method Body",null)); if (methodDecl.body != null) { if (callingThis || callingSuper) { convert(iter.next()); } else if (isConstructor && esc) { // FIXME - need assumptions from default super constructor } if (isConstructor && esc && !callingThis) { boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { addInstanceInitialization(); } finally { checkAccessEnabled = pv; } } while (iter.hasNext()) { JCStatement s = iter.next(); //System.out.println("CONVERTING " + s.toString()); convert(s); } } JCBlock newMainBody = popBlock(0,methodDecl.body == null ? methodDecl: methodDecl.body,check); // The outerTryStatement just has a finally clause in which the // postconditions and exceptional postconditions are checked. if (!pureCopy) { outerFinalizeStats.add( comment(methodDecl,"Check Postconditions",null)); addPostConditions(outerFinalizeStats); axiomBlock = null; } String v = JmlOption.value(context,JmlOption.FEASIBILITY); if (esc && (v.equals("all") || v.equals("exit") || v.equals("debug"))) { addAssumeCheck(methodDecl,outerFinalizeStats,Strings.atExitAssumeCheckDescription); } JCCatch c; { // global catch block JCVariableDecl ex = treeutils.makeVarDef(syms.exceptionType,names.fromString("_JML__ex"),methodDecl.sym,methodDecl.pos); pushBlock(); addStat(treeutils.makeAssignStat(methodDecl.pos, treeutils.makeIdent(methodDecl.pos,exceptionSym), treeutils.makeIdent(methodDecl.pos, ex.sym))); addStat(M.at(methodDecl.pos).Throw(treeutils.makeIdent(methodDecl.pos, ex.sym))); JCBlock bl = popBlock(0,methodDecl); c = M.at(methodDecl.pos).Catch(ex, bl); } JCTry outerTryStatement = M.at(methodDecl).Try( newMainBody, esc ? List.<JCCatch>nil() : List.<JCCatch>of(c), M.Block(0, outerFinalizeStats.toList())); // This block is to create the try-catch block for converting PreconditionEntry to Precondition if (rac && JmlOption.isOption(context, JmlOption.RAC_PRECONDITION_ENTRY)) { // precondition catch block JCBlock tryBlock = M.at(methodDecl).Block(0, List.<JCStatement>of(outerTryStatement)); ClassSymbol preex = ClassReader.instance(context).loadClass(names.fromString("org.jmlspecs.utils.JmlAssertionError$PreconditionEntry")); JCVariableDecl ex = treeutils.makeVarDef(preex.type,names.fromString("_JML__ex"),methodDecl.sym,methodDecl.pos); pushBlock(); JCMethodInvocation m = treeutils.makeUtilsMethodCall(methodDecl.pos, "convertPrecondition", treeutils.makeIdent(methodDecl.pos,ex.sym)); addStat(M.at(methodDecl.pos).Exec(m)); JCBlock catchBlock = popBlock(0,methodDecl); JCCatch cc = M.at(methodDecl.pos).Catch(ex, catchBlock); JCTry preconditionTryStatement = M.at(methodDecl).Try( tryBlock, List.<JCCatch>of(cc), null); outerTryStatement = preconditionTryStatement; } initialStatements.add(outerTryStatement); return M.at(methodDecl).Block(0,initialStatements.toList()); } catch (JmlNotImplementedException e) { throw e; } catch (JmlInternalAbort e) { return null; } catch (RuntimeException e) { String message = e.getMessage(); if (message == null) message = "Internal exception: " + e.getClass(); StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); message = message + JmlTree.eol + sw.toString(); Log.instance(context).error("jml.internal.notsobad",message); return null; } finally { this.assumeCheckCount = prevAssumeCheckCount; this.methodDecl = prev; this.classDecl = prevClass; this.initialStatements = prevStats; this.currentThisId = savedThisId; this.currentThisExpr = savedThisExpr; this.resultSym = savedResultSym; this.exceptionSym = savedExceptionSym; this.terminationSym = savedTerminationSym; this.oldStatements = savedOldStatements; this.currentStatements = null; this.paramActuals = savedParamActuals; log.useSource(prevSource); this.completedInvariants = savedCompletedInvariants; this.inProcessInvariants = savedInProcessInvariants; this.enclosingMethod = savedEnclosingMethod; this.enclosingClass = savedEnclosingClass; this.defaultOldLabel = savedOldLabel; this.currentArithmeticMode = savedArithmeticMode; Main.instance(context).popOptions(); } } /** Internal method to do the method body conversion */ protected JCBlock convertMethodBodyNoInitModel(JmlMethodDecl pmethodDecl, JmlClassDecl pclassDecl) { assumeCheckCount = 0; this.methodDecl = pmethodDecl; this.classDecl = pclassDecl != null ? pclassDecl : utils.getOwner(methodDecl) ; this.initialStatements = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> outerFinalizeStats = new ListBuffer<JCStatement>(); boolean isConstructor = methodDecl.sym.isConstructor(); oldStatements = initialStatements; currentStatements = initialStatements; if (methodDecl.restype.type.getTag() != TypeTag.VOID) { // The compiler complains that the result variable might not be // initialized on every path, even in programs in which it appears // obvious that it is. So we initialize it here to a null-like value. JCVariableDecl d = treeutils.makeVarDef(methodDecl.restype.type,resultName,methodDecl.sym, treeutils.makeZeroEquivalentLit(methodDecl.pos,methodDecl.restype.type)); resultSym = d.sym; initialStatements.add(d); } initialize2(0L); // if (allocSym == null) { // allocSym = treeutils.makeVarSymbol(0, names.fromString(Strings.allocName), syms.intType, classDecl.pos); // allocSym.owner = classDecl.sym; // isAllocSym = treeutils.makeVarSymbol(0, names.fromString(Strings.isAllocName), syms.booleanType, classDecl.pos);; // isAllocSym.owner = classDecl.sym; // // if (esc) { // // THIS is an id that is a proxy for the this object on which a method is called; // // we need to distinguish it from uses of 'this' in the text // // FIXME - should make this NonNull //// VarSymbol THISSym = treeutils.makeVarSymbol(Flags.STATIC,names.fromString(Strings.thisName),classDecl.sym.type, Position.NOPOS); //// THISSym.owner = classDecl.sym; //// this.currentThisId = treeutils.makeIdent(classDecl.pos,THISSym); //// this.thisIds.put(classDecl.sym, this.currentThisId); //// exprBiMap.put(this.currentThisId, this.currentThisId); // this.currentThisId = makeThisId(classDecl.pos,classDecl.sym); // this.currentThisExpr = this.currentThisId; // } else { // rac // // For RAC we use the actual 'this' FIXME - not sure about this design - perhaps just need to be cautious about what is translated and what is not // this.currentThisId = treeutils.makeIdent(classDecl.pos, classDecl.thisSymbol); // this.currentThisExpr = this.currentThisId; // this.thisIds.put(classDecl.sym, currentThisId); // } // } // if (esc && (isConstructor || !utils.isJMLStatic(methodDecl.sym))) { // currentStatements = initialStatements; // JCExpression e = treeutils.makeNeqObject(methodDecl.pos,currentThisExpr,treeutils.nullLit); // addAssume(methodDecl,Label.IMPLICIT_ASSUME,e); // addAssume(classDecl,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(classDecl,currentThisExpr,classDecl.type)); // } pushBlock(); // FIXME - should we have a try block // if (!pureCopy) { // // outerFinalizeStats.add( comment(methodDecl,"Check Postconditions")); // addPrePostConditions(initialStatements, outerFinalizeStats); // // pushBlock(); // addAssumeCheck(methodDecl,currentStatements,preconditionAssumeCheckDescription); // FIXME - use a smaller highlight range than the whole method - perhaps the specs? // JCStatement preconditionAssumeCheck = popBlock(0,methodDecl); // addStat(initialStatements,preconditionAssumeCheck); // // } addStat( comment(methodDecl,"Method Body",null)); if (methodDecl.body != null) { Iterator<JCStatement> iter = methodDecl.body.stats.iterator(); while (iter.hasNext()) { scan(iter.next()); } } JCBlock newMainBody = popBlock(0,methodDecl.body == null ? methodDecl: methodDecl.body); JCCatch c; { // global catch block JCVariableDecl ex = treeutils.makeVarDef(syms.exceptionType,names.fromString("_JML__ex"),methodDecl.sym,methodDecl.pos); pushBlock(); addStat(treeutils.makeAssignStat(methodDecl.pos, treeutils.makeIdent(methodDecl.pos,exceptionSym), treeutils.makeIdent(methodDecl.pos, ex.sym))); addStat(M.at(methodDecl.pos).Throw(treeutils.makeIdent(methodDecl.pos, ex.sym))); JCBlock bl = popBlock(0,methodDecl); c = M.at(methodDecl.pos).Catch(ex, bl); } JCTry outerTryStatement = M.at(methodDecl).Try( newMainBody, esc ? List.<JCCatch>nil() : List.<JCCatch>of(c), M.Block(0, outerFinalizeStats.toList())); initialStatements.add(outerTryStatement); return M.at(methodDecl).Block(0,initialStatements.toList()); } // Generic node translation methods. Most of these may add statements to // 'currentStatements'. // DESIGN NOTE: These routines expect all visit nodes to return a value in // 'result' and (for JCExpression derivatives) in 'eresult'. However, in // all modes except pureCopy, there may be side-effects of other statements // or declarations being produced; in addition, the class of the returned // node may be different than that of the argument, because of the // rac or esc translation. /** (Public API) Converts a non-expression AST, returning the converted tree; * this may be called externally on ClassDecl and CompilationUnit * trees, but should not be called outside of JmlAssertionAdder * on trees lower in the AST. In purecopy mode, T can be any JCTree derived * type; but otherwise, it may only be either JCTree or JCExpression * (possibly JCStatement) */ @SuppressWarnings("unchecked") public @Nullable <T extends JCTree> T convert(@Nullable T tree) { if (tree == null) { result = null; return null; } scan(tree); if (localVariables.isEmpty()) exprBiMap.put(tree, result); return (T)result; } /** Returns a translation of a list of tree, possibly pushing additional * statements onto 'currentStatements'; the same restrictions on T apply * as above. */ public @Nullable <T extends JCTree> List<T> convert(@Nullable List<T> trees) { if (trees==null) return null; ListBuffer<T> newlist = new ListBuffer<T>(); for (T t: trees) { newlist.add(convert(t)); } return newlist.toList(); } /** Returns a translation of a list of tree, possibly pushing additional * statements onto 'currentStatements'; the same restrictions on T apply * as above. */ public @Nullable <T extends JCTree> java.util.List<T> convert(@Nullable java.util.List<T> trees) { if (trees==null) return null; java.util.List<T> newlist = new LinkedList<T>(); for (T t: trees) { newlist.add(convert(t)); } return newlist; } /** Returns a translation of an expression, possibly pushing additional * statements onto 'currentStatements' */ protected @Nullable JCExpression convertAssignable(@Nullable JCExpression tree, JCExpression receiver, boolean isInJML) { // Normally this method is called on left-values, in which case tree is never just 'this'; if 'this' appears in the // expression it is as a receiver and is replaced by the receiver. // But it also called to determine accessiblity, in which case it can be called on normal expressions, // and 'this' can occur as a normal expression. So we treat it as a special case. if (tree instanceof JCIdent && ((JCIdent)tree).name == names._this) return tree; eresult = null; // Just so it is initialized in case assignment is forgotten boolean savedT = translatingJML; boolean savedS = splitExpressions; boolean savedA = convertingAssignable; JCExpression savedC = condition; JCExpression savedThis = currentThisExpr; JCIdent savedId = currentThisId; boolean pv = checkAccessEnabled; try { translatingJML = isInJML; condition = treeutils.trueLit; splitExpressions = false; convertingAssignable = true; currentThisExpr = receiver; currentThisId = (JCIdent)receiver; // FIXME - do we really need an ID checkAccessEnabled= false; if (tree != null) { super.scan(tree); if (rac && eresult != null && eresult.type != null && jmltypes.isJmlType(eresult.type)) eresult.type = jmltypes.repSym((JmlType)eresult.type).type; if (localVariables.isEmpty()) exprBiMap.put(tree,eresult); } } finally { translatingJML = savedT; splitExpressions = savedS; convertingAssignable = savedA; condition = savedC; currentThisExpr = savedThis; currentThisId = savedId; checkAccessEnabled = pv; } return eresult; } /** Returns a translation of an expression, possibly pushing additional * statements onto 'currentStatements' */ public @Nullable JCExpression convertExpr(@Nullable JCExpression tree) { eresult = null; // Just so it is initialized in case assignment is forgotten if (tree != null) { super.scan(tree); if (rac && eresult != null && eresult.type != null && jmltypes.isJmlType(eresult.type)) eresult.type = jmltypes.repSym((JmlType)eresult.type).type; if (localVariables.isEmpty()) exprBiMap.put(tree,eresult); } return eresult; } /** Returns a translation of a list of expression, possibly pushing additional * statements onto 'currentStatements' */ public @Nullable List<JCExpression> convertExprList(@Nullable List<? extends JCExpression> trees) { if (trees==null) return null; ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (JCExpression t: trees) { scan(t); if (eresult == null) eresult = t; newlist.add(eresult); if (localVariables.isEmpty()) exprBiMap.put(t,eresult); } return newlist.toList(); } /** Does a pure copy of the tree; once convertCopy is called on a node, child * calls to convertExpr or convert will also be in pureCopy mode. */ public @Nullable <T extends JCTree> T convertCopy(@Nullable T tree) { boolean savedCopy = pureCopy; boolean savedSplit = splitExpressions; try { pureCopy = true; splitExpressions = false; return convert(tree); } finally { pureCopy = savedCopy; splitExpressions = savedSplit; } } public @Nullable <T extends JCTree> T convertCopyNoEmit(@Nullable T tree) { boolean savedCopy = pureCopy; boolean savedSplit = splitExpressions; ListBuffer<JCStatement> prev = currentStatements; currentStatements = null; try { pureCopy = true; splitExpressions = false; return convert(tree); } finally { pureCopy = savedCopy; splitExpressions = savedSplit; currentStatements = prev; } } /** Does a pure copy of the list of trees */ public <T extends JCTree> List<T> convertCopy(List<T> trees) { if (trees == null) return null; boolean savedCopy = pureCopy; boolean savedSplit = splitExpressions; try { pureCopy = true; splitExpressions = false; pushBlock(); ListBuffer<T> newlist = new ListBuffer<T>(); for (T t: trees) { newlist.add(convert(t)); } popBlock(); return newlist.toList(); } finally { pureCopy = savedCopy; splitExpressions = savedSplit; } } /** Does a pure copy of the list of trees */ public <T extends JCTree> java.util.List<T> convertCopy(java.util.List<T> trees) { boolean savedCopy = pureCopy; boolean savedSplit = splitExpressions; try { pureCopy = true; splitExpressions = false; java.util.List<T> newlist = new java.util.LinkedList<T>(); for (T t: trees) { newlist.add(convert(t)); } return newlist; } finally { pureCopy = savedCopy; splitExpressions = savedSplit; } } /** Translates an AST as JML - that is, assuming that the AST is pure; * this call is used to switch to the translatingJML mode, setting the * condition and isPostcondition to the given values, * restoring isPostcondition and translatingJML upon return. */ public @Nullable JCExpression convertJML(@Nullable JCExpression that, JCExpression condition, boolean isPostcondition) { if (that == null) return null; boolean savedp = this.isPostcondition; boolean savedt = this.translatingJML; boolean savedSplit = this.splitExpressions; boolean savedCA = this.checkAccessEnabled; IArithmeticMode savedArithmeticMode = this.currentArithmeticMode; JCExpression savedc = this.condition; try { if (!translatingJML) { currentArithmeticMode = Arithmetic.Math.instance(context).defaultArithmeticMode( methodDecl != null ? methodDecl.sym : classDecl.sym,true); } this.isPostcondition = isPostcondition; this.condition = condition; this.translatingJML = true; this.splitExpressions = rac; this.checkAccessEnabled = false; return convertExpr(that); } finally { this.isPostcondition = savedp; this.translatingJML = savedt; this.splitExpressions = savedSplit; this.condition = savedc; this.currentArithmeticMode = savedArithmeticMode; this.checkAccessEnabled = savedCA; } } /** Begins JML scanning for a non-postcondition */ public @Nullable JCExpression convertJML(@Nullable JCExpression that) { return convertJML(that, treeutils.trueLit, false); } /** Applies convertJML to a list of expressions, returning the new list. */ public @Nullable List<JCExpression> convertJML(@Nullable List<JCExpression> trees) { if (trees==null) return null; else { ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (JCExpression tree: trees) { convertJML(tree, treeutils.trueLit, false); newlist.add(eresult); } return newlist.toList(); } } private Map<Symbol,Symbol> mapSymbols = new HashMap<Symbol,Symbol>(); protected @NonNull <T extends Symbol> T convertSymbol(T sym) { Symbol s = mapSymbols.get(sym); return s == null ? sym : (T)s; } protected <T extends Symbol> void putSymbol(T sym, T newsym) { mapSymbols.put(sym, newsym); } /** Translates a block, but without adding the block to the statement list; * any side-effect statements are placed within the new block. */ protected @Nullable JCBlock convertBlock(@Nullable JCBlock block) { if (block == null) return null; pushBlock(); try { scan(block.stats); } finally { return popBlock(block.flags,block); } } /** Translates a list of statements, returning a block containing the translations */ protected JCBlock convertIntoBlock(DiagnosticPosition pos, List<JCStatement> stats) { pushBlock(); try { scan(stats); } finally { return popBlock(0,pos); } } /** Translates one statement, returning a block containing the translation * (including any statements it spawns); if the statement is a block, then * the block's statements are translated, so there is not an excess nested * block. */ protected JCBlock convertIntoBlock(DiagnosticPosition pos, JCStatement stat) { ListBuffer<JCStatement> check = pushBlock(); try { if (stat instanceof JCBlock) scan(((JCBlock)stat).stats); else scan(stat); } finally { return popBlock(0,pos,check); } } /** Start collecting statements for a new block; push currentStatements * onto a stack for later use */ protected ListBuffer<JCStatement> pushBlock() { ListBuffer<JCStatement> temp = currentStatements; statementStack.add(0,currentStatements); currentStatements = new ListBuffer<JCStatement>(); //if (temp != null) System.out.println("PUSHING " + temp.hashCode() + " NEW " + currentStatements.hashCode()); return temp; } /** Wrap the active currentStatements as a block (which is returned) and * then resume collecting statements with currentStatements value on the * top of the stack (which is also removed from the stack) */ protected JCBlock popBlock(long flags, DiagnosticPosition pos) { JCBlock b = M.at(pos).Block(flags, currentStatements.toList()); //System.out.println("POPPING " + currentStatements.hashCode()); currentStatements = statementStack.removeFirst(); return b; } protected JCBlock popBlock(long flags, DiagnosticPosition pos, ListBuffer<JCStatement> check) { JCBlock b = M.at(pos).Block(flags, currentStatements.toList()); //System.out.println("POPPING " + currentStatements.hashCode()); currentStatements = statementStack.removeFirst(); if (check != currentStatements) { log.error("jml.internal", "MISMATCHED BLOCKS"); } return b; } protected LinkedList<ListBuffer<JCStatement>> markBlock() { LinkedList<ListBuffer<JCStatement>> temp = new LinkedList<ListBuffer<JCStatement>>(); temp.addAll(statementStack); temp.add(0,currentStatements); return temp; } protected boolean checkBlock(LinkedList<ListBuffer<JCStatement>> temp) { Iterator<ListBuffer<JCStatement>> iter = temp.iterator(); ListBuffer<JCStatement> t = iter.next(); if (t != currentStatements) return false; Iterator<ListBuffer<JCStatement>> iter2 = statementStack.iterator(); while (iter2.hasNext() && iter.hasNext()) { if (iter2.next() != iter.next()) return false; } return !iter2.hasNext() && !iter.hasNext(); } /** Pop and ignore the content of currentStatements. */ protected void popBlock() { currentStatements = statementStack.removeFirst(); } public JCExpression makeAssertionOptional(JCExpression e) { if (rac) return e; JCIdent id = newTemp(e,syms.booleanType); e = treeutils.makeOr(e.getPreferredPosition(),id,e); return e; } /** Add a statement at the end of the active currentStatements sequence, * returning the argument */ protected <T extends JCStatement> T addStat(T stat) { if (currentStatements != null) currentStatements.add(stat); else if (classDefs != null) classDefs.add(stat); else log.error(stat.pos, "jml.internal", "No place to add a statement"); return stat; } /** Add a statement at the end of the given buffer of statements, * returning the statement added */ protected <T extends JCStatement> T addStat(ListBuffer<JCStatement> stats, T stat) { stats.add(stat); return stat; } /** This generates a JmlExpressionStatement comment statement with the given string as * text; the statement is not added to any statement list. */ protected JmlStatementExpr comment(DiagnosticPosition pos, String s, /*@ nullable */JavaFileObject source) { if (s.contains("\n") || s.contains("\r")) { s = s.replace("\r\n"," ").replace('\r', ' ').replace('\n', ' '); } JmlStatementExpr st = M.at(pos).JmlExpressionStatement(JmlTokenKind.COMMENT,null,M.at(pos).Literal(s)); st.associatedSource = source; return st; } /** This generates a comment statement whose content is the * given JCTree, pretty-printed; the statement is not added to any statement list */ public JmlStatementExpr comment(JCTree t) { String s = JmlPretty.write(t,false); int k = s.indexOf(JmlTree.eol); // No multi-line comments if (k == -1) k = s.length(); final int maxlength = 80; if (k > maxlength) { s = s.substring(0,k) + " ..."; } JavaFileObject source = null; if (t instanceof JmlStatementExpr) source = ((JmlStatementExpr)t).source; return comment(t,s,source); } /** Issue an internal error message and throw an exception. */ public void error(DiagnosticPosition pos, String msg) { log.error(pos, "esc.internal.error", msg); throw new JmlInternalError(msg); } /** Issue an internal warning message and throw an exception. */ public void warning(DiagnosticPosition pos, String key, Object ... msg) { log.warning(pos, key, msg); } // FIXME - this is a hack - fix and document // These are used so that we don't send repeated notImplemented messages static Set<String> racMessages = new HashSet<String>(); static Set<String> escMessages = new HashSet<String>(); /** Issues a diagnostic message (note) containing the message in the given * exception. */ protected void notImplemented(String location, JmlNotImplementedException source) { String message = location + source.getMessage(); String key = source.pos.getPreferredPosition() + message; if (rac ? !racMessages.add(key) : !escMessages.add(key)) return; log.note(source.pos, rac ? "rac.not.implemented" : "esc.not.implemented", message); } public void notImplemented(String location, JmlNotImplementedException source, JavaFileObject file) { String message = location + source.getMessage(); String key = source.pos.getPreferredPosition() + message; JavaFileObject prev = file == null ? null : log.useSource(file); if (rac ? racMessages.add(key) : escMessages.add(key)) { log.note(source.pos, rac ? "rac.not.implemented" : "esc.not.implemented", message); } if (file != null) log.useSource(prev); } /** Issues a diagnostic message (note) containing the given message. */ public void notImplemented(DiagnosticPosition pos, String message, JavaFileObject source) { String key = pos.getPreferredPosition() + message; if (rac ? !racMessages.add(key) : !escMessages.add(key)) return; JavaFileObject prev = log.useSource(source); log.note(pos, rac ? "rac.not.implemented" : "esc.not.implemented", message); log.useSource(prev); } public void notImplemented(DiagnosticPosition pos, String message) { String key = pos.getPreferredPosition() + message; if (rac ? !racMessages.add(key) : !escMessages.add(key)) return; log.note(pos, rac ? "rac.not.implemented" : "esc.not.implemented", message); } /** Adds an assertion with the given label and (already translated) expression * to 'currentStatements'. 'codepos' is the position within the code where * the assertion is triggered; log.currentSource() is used as the sourcefile * in which 'codepos' lies. 'associatedSource' and 'associatedPos' are the * location of the specification clause from which this assertion derives, * if any. * The args are arguments for the resource key giving the error message * corresponding to the given Label. * Returns null if no assertion was added */ public @Nullable JCStatement addAssert( boolean trace, DiagnosticPosition codepos, // FIXME _ document whether nullable and behavior Label label, JCExpression translatedExpr, @Nullable DiagnosticPosition associatedPos, @Nullable JavaFileObject associatedSource, @Nullable JCExpression info, Object ... args) { if (label != Label.ASSUME_CHECK && JmlOption.value(context,JmlOption.FEASIBILITY).equals("debug")) { addAssumeCheck(translatedExpr,currentStatements,"Extra-Assert"); } boolean isTrue = treeutils.isTrueLit(translatedExpr); boolean isFalse = treeutils.isFalseLit(translatedExpr); if (isTrue) return null; // Literally true - don't even add the statement if (nowarns.suppress(log.currentSource(), codepos == null ? Position.NOPOS : codepos.getPreferredPosition(), label.toString())) return null; if (associatedPos != null) { if (nowarns.suppress(associatedSource == null ? log.currentSourceFile() : associatedSource, associatedPos.getPreferredPosition(), label.toString())) return null; } String assertID = Strings.assertPrefix + (++assertCount); if (assertCount == 1149) Utils.stop(); Name assertname = names.fromString(assertID); JavaFileObject dsource = log.currentSourceFile(); JCVariableDecl assertDecl = treeutils.makeVarDef(syms.booleanType,assertname,methodDecl == null? classDecl.sym : methodDecl.sym,translatedExpr); assertDecl.mods.flags |= Flags.FINAL; assertDecl.sym.flags_field |= Flags.FINAL; if (esc) { String extra = Strings.empty; for (Object o: args) { extra = extra + " " + o.toString(); } JmlStatementExpr st = treeutils.makeAssert(codepos,label,treeutils.makeIdent(translatedExpr.pos,assertDecl.sym)); st.source = dsource; st.associatedPos = associatedPos == null ? Position.NOPOS : associatedPos.getPreferredPosition(); st.associatedSource = associatedSource; st.optionalExpression = info; st.id = assertID; st.description = extra.isEmpty() ? null : extra; treeutils.copyEndPosition(st.expression, translatedExpr); treeutils.copyEndPosition(st, translatedExpr); // Note that the position of the expression may be that of the associatedPos, not of the original assert, if there even is one if (trace) { JCExpression newexpr = convertCopy(translatedExpr); assertDecl.init = newexpr; addTraceableComment(st,translatedExpr,label + " assertion: " + translatedExpr.toString()); } currentStatements.add(assertDecl); currentStatements.add(st); return st; } if (rac) { JCExpression racarg = null; if (args != null && args.length > 0 && args[0] instanceof JCExpression) { racarg = (JCExpression)args[0]; args = new Object[0]; } JCDiagnostic diag = JCDiagnostic.Factory.instance(context).warning(log.currentSource(), codepos, "rac." + label, args); String msg = (showRacSource? diag.toString() : diag.noSource()).replace("warning: ", ""); JCExpression emsg; if (racarg != null) { int k = msg.indexOf(JmlTree.eol); if (k < 0) k = msg.indexOf('\n'); if (k >= 0) { String m1 = msg.substring(0,k); String m2 = msg.substring(k); emsg = treeutils.makeStringLiteral(translatedExpr.pos,m1 + ": "); emsg = treeutils.makeUtilsMethodCall(racarg.pos, "concat", emsg, racarg); emsg = treeutils.makeUtilsMethodCall(racarg.pos, "concat", emsg, treeutils.makeStringLiteral(translatedExpr.pos,m2)); } else { emsg = treeutils.makeStringLiteral(translatedExpr.pos,msg + ": "); emsg = treeutils.makeUtilsMethodCall(racarg.pos, "concat", emsg, racarg); } } else { emsg = treeutils.makeStringLiteral(translatedExpr.pos,msg); } if (associatedPos != null) { diag = JCDiagnostic.Factory.instance(context).warning( new DiagnosticSource(associatedSource != null ? associatedSource : log.currentSourceFile(),null), associatedPos, Utils.testingMode || !showRacSource ? "jml.associated.decl" : "jml.associated.decl.cf", utils.locationString(codepos.getPreferredPosition())); String msg2 = JmlTree.eol + (showRacSource? diag.toString() : diag.noSource()).replace("warning: ", ""); emsg = treeutils.makeUtilsMethodCall(emsg.pos, "concat", emsg, treeutils.makeStringLiteral(translatedExpr.pos,msg2)); } JCStatement st; if (JmlOption.isOption(context, JmlOption.RAC_COMPILE_TO_JAVA_ASSERT)) { st = M.at(codepos).Assert(translatedExpr, emsg); } else { if (info != null) { emsg = info; } st = assertFailure(emsg,codepos,label); if (!isFalse) st = M.at(codepos).If( treeutils.makeNot(codepos == null ? Position.NOPOS : codepos.getPreferredPosition(), treeutils.makeIdent(translatedExpr.pos,assertDecl.sym)), st, null); } addStat(comment(translatedExpr,label + " assertion: " + translatedExpr.toString(),associatedSource)); currentStatements.add(assertDecl); currentStatements.add(st); return st; } return null; } /** Adds an assertion with the given label and (already translated) expression * to 'currentStatements'. 'codepos' is the position within the code where * the assertion is triggered; log.currentSource() is used as the sourcefile * in which 'codepos' lies. There is no associated position or extra information. * The args are arguments for the resource key giving the error message * corresponding to the given Label. */ public JCStatement addAssert( DiagnosticPosition codepos, Label label, JCExpression expr, Object ... args) { return addAssert(true,codepos,label,expr,null,null,null,args); } /** Creates a statement at which we can check feasibility */ protected void addAssumeCheck(JCTree item, ListBuffer<JCStatement> list, String description) { addAssumeCheck(item,list,description,treeutils.trueLit); } public static boolean useAssertCount = true; /** Creates a statement at which we can check feasibility */ protected void addAssumeCheck(JCTree item, ListBuffer<JCStatement> list, String description, JCExpression predicate) { if (!esc) return; // We create feasibility check points by adding assertions of the // form assumeCheckVar != n, for different values of n > 0. // Then for normal checking of the method, we assert assumeCheckVar == 0 // so all the introduced asserts are trivially true. // But plater we can pop the assumeCheckVar == 0 and add // assumeCHeckVar == k, to check feasibility at point k. ++assumeCheckCount; java.util.List<JmlStatementExpr> descs = assumeChecks.get(methodDecl); if (descs == null) assumeChecks.put(methodDecl, descs = new LinkedList<JmlStatementExpr>()); if (useAssertCount) { JCIdent id = treeutils.makeIdent(item.pos, assumeCheckSym); JCExpression bin = treeutils.makeBinary(item.pos,JCTree.Tag.NE,treeutils.intneqSymbol,id,treeutils.makeIntLiteral(item.pos,assumeCheckCount)); if (!treeutils.isTrueLit(predicate)) bin = treeutils.makeImplies(item.pos, predicate, bin); ListBuffer<JCStatement> prev = currentStatements; currentStatements = list; JmlStatementExpr a = (JmlStatementExpr)addAssert(item, Label.ASSUME_CHECK, bin); a.description = description; a.source = (item instanceof JmlTree.JmlSource) ? ((JmlTree.JmlSource)item).source() : null; descs.add(a); currentStatements = prev; } else { JmlStatementExpr c = comment(item, "ACHECK " + assumeCheckCount, log.currentSourceFile()); c.description = description; c.id = "ACHECK " + assumeCheckCount; addStat(c); } } /** Adds an assertion with the given label and (already translated) expression * to 'currentStatements'. 'codepos' is the position within the code where * the assertion is triggered; log.currentSource() is used as the sourcefile * in which 'codepos' lies. The last two arguments give the file and position * within the file of the associated specification that is potentially violated. * The args are arguments for the resource key giving the error message * corresponding to the given Label. */ public JCStatement addAssert( DiagnosticPosition codepos, Label label, JCExpression expr, DiagnosticPosition associatedPos, JavaFileObject associatedSource, Object ... args) { return addAssert(true,codepos,label,expr,associatedPos,associatedSource,null,args); } /** Creates a call of org.jmlspecs.utils.Utils.assertionFailure(s), where * s is a literal containing the value of the argument, for RAC translations * @param sp the string to make into the literal argument of the call * @param pos the character position of the created AST * @return an assert statement indication an assertion failure */ protected JCStatement assertFailure(JCExpression sp, DiagnosticPosition pos, Label label) { String n = label.info(); JCFieldAccess m; if (rac && label == Label.PRECONDITION && JmlOption.isOption(context, JmlOption.RAC_PRECONDITION_ENTRY)) { n = "PreconditionEntry"; m = findUtilsMethod(pos,org.jmlspecs.utils.Utils.ASSERTION_FAILURE_EX); } else { m = findUtilsMethod(pos,org.jmlspecs.utils.Utils.ASSERTION_FAILURE); } JCExpression c = M.at(pos).Apply(null,m,List.<JCExpression>of(sp,treeutils.makeStringLiteral(0,n))).setType(syms.voidType); return M.at(pos).Exec(c); } // protected JCStatement assertFailure(JCExpression sp, DiagnosticPosition pos, Label label, JCExpression arg) { // JCFieldAccess m = findUtilsMethod(pos,org.jmlspecs.utils.Utils.ASSERTION_FAILURE); // JCExpression c = M.at(pos).Apply(null,m,List.<JCExpression>of(sp,treeutils.makeStringLiteral(0,label.info()))).setType(syms.voidType); // return M.at(pos).Exec(c); // } /** Creates an assumption that two expressions are equal, adding the assumption to 'currentStatements'. */ public JCStatement addAssumeEqual( DiagnosticPosition pos, Label label, JCExpression lhs, JCExpression rhs) { return addAssume(pos,label,treeutils.makeBinary(pos.getPreferredPosition(),JCTree.Tag.EQ,lhs,rhs),null,null,null); } /** Creates an assumption, adding it to 'currentStatements' */ public JCStatement addAssume( DiagnosticPosition pos, Label label, JCExpression ex) { return addAssume(pos,label,ex,null,null,null,""); } /** Creates an assumption with an associated declaration (perhaps in a different file), * adding it to 'currentStatements' (does nothing if esc and rac are false)*/ public JCStatement addAssume( DiagnosticPosition pos, Label label, JCExpression ex, @Nullable DiagnosticPosition associatedPos, @Nullable JavaFileObject associatedSource, Object ... args) { return addAssume(pos,label,ex,associatedPos,associatedSource,null,args); } /** Creates an assumption with an associated declaration (perhaps in a * different file), adding it to 'currentStatements' */ public JCStatement addAssume( DiagnosticPosition pos, Label label, JCExpression translatedExpr, @Nullable DiagnosticPosition associatedPosition, @Nullable JavaFileObject associatedSource, @Nullable JCExpression info, Object ... args) { JCStatement stt = null; if (esc) { if (translatedExpr.toString().equals("e.cause != null")) Utils.stop(); if (label != Label.ASSUME_CHECK && currentStatements != null && JmlOption.value(context,JmlOption.FEASIBILITY).equals("debug")) { addAssumeCheck(translatedExpr,currentStatements,"Extra-Assume"); } JmlStatementExpr st = treeutils.makeAssume(pos,label,translatedExpr); st.source = log.currentSourceFile(); st.associatedPos = associatedPosition == null ? Position.NOPOS : associatedPosition.getPreferredPosition(); st.associatedSource = associatedSource; st.optionalExpression = info; st.id = Strings.assumePrefix + (++assertCount); if (currentStatements != null) currentStatements.add(st); else classDefs.add(st); stt = st; } if (rac && racCheckAssumeStatements) { stt = addAssert(true,pos,label,translatedExpr,associatedPosition,associatedSource,info,args); } return stt; } /** Creates an AST for calling a method with the given name from the * org.jmlspecs.utils.Utils class (that is available at runtime) * @param methodName the name of the method * @return the corresponding AST */ protected JCFieldAccess findUtilsMethod(DiagnosticPosition pos, String methodName) { JCIdent utilsClassIdent = M.Ident(utilsClass); Name n = names.fromString(methodName); Scope.Entry e = utilsClass.members().lookup(n); Symbol ms = e.sym; if (e.sym == null) { error(pos,"Method is not found in the runtime library: " + methodName); return null; // does not return } else { JCFieldAccess m = M.at(pos).Select(utilsClassIdent,n); m.sym = ms; m.type = m.sym.type; return m; } } /** Creates an expression AST for a call of the given static method (from * org.jmlspecs.utils.Utils). * @param methodName the name of the method to call * @param translatedArgs the ASTs which are the (already translated) arguments * @return the resulting AST */ protected JCExpression methodCallUtilsExpression(DiagnosticPosition pos, String methodName, JCExpression ... translatedArgs) { JCFieldAccess m = findUtilsMethod(pos,methodName); List<JCExpression> args = new ListBuffer<JCExpression>().appendArray(translatedArgs).toList(); JCMethodInvocation c = M.at(pos).Apply(null,m,args); if (m.type instanceof Type.MethodType){ c.setType(((Type.MethodType)m.type).restype); } else if (m.type instanceof Type.ForAll) { Type.ForAll tfa = (Type.ForAll)m.type; c.setType(((Type.MethodType)tfa.qtype).restype); } else { error(pos,"Unknown method type in methodCallUtilsExpression: " + methodName + " " + m.type.getClass()); } return c; } /** Creates a statement AST for a call of the given method (from * org.jmlspecs.utils.Utils). * @param methodName the name of the method to call * @param translatedArgs the ASTs which are the (already translated) arguments * @return the resulting AST */ protected JCStatement methodCallUtilsStatement(DiagnosticPosition pos, String methodName, JCExpression ... translatedArg) { JCExpression c = methodCallUtilsExpression(pos, methodName, translatedArg); return M.at(pos).Exec(c); } protected JCVariableDecl newTempDecl(DiagnosticPosition pos, Type t) { Name n = M.Name(Strings.tmpVarString + (++count)); JCVariableDecl d = treeutils.makeVarDef(t, n, esc ? null : methodDecl.sym , esc ? Position.NOPOS : pos.getPreferredPosition()); // FIXME - see note below return d; } /** Creates a declaration for a unique temporary name with the given type and position, * with no initialization, adds it to currentStatements and returns an Ident * for the new symbol. */ protected JCIdent newTemp(DiagnosticPosition pos, Type t) { JCVariableDecl d = newTempDecl(pos,t); // FIXME - see note below // We mark all temporaries as final, as an indication that they will // be used only once. // FIXME - do this or not? can we avoid the esc? check above? // d.mods.flags |= Flags.FINAL; // d.sym.flags_field |= Flags.FINAL; currentStatements.add(d); JCIdent id = M.at(pos).Ident(d.sym); return id; } /** Creates a declaration for a unique temporary initialized to the given expression. */ public JCIdent newTemp(JCExpression expr) { return newTemp(Strings.tmpVarString + (++count),expr); } public JCIdent newTemp(String name, /*@ non_null */JCExpression expr) { return newTemp(expr.pos, name, expr); } /** Creates a declaration for the given name initialized to the given expression. */ public JCIdent newTemp(int pos, String name, /*@ non_null */JCExpression expr) { Name n = M.Name(name); // By having the owner be null, the BasicBlocker2 does not append any unique-ifying suffix - FIXME - does this affect RAC? JmlVariableDecl d = (JmlVariableDecl)treeutils.makeVarDef( expr.type.getTag() == TypeTag.BOT ? syms.objectType : expr.type, n, esc ? null : methodDecl != null ? methodDecl.sym : classDecl.sym, expr); // d.sym.flags_field |= Flags.FINAL; // d.mods.flags |= Flags.FINAL; d.pos = pos; d.sym.pos = Position.NOPOS; // We set the position to NOPOS so that the temporary name is not further encoded JCIdent id = treeutils.makeIdent(expr.getStartPosition(),d.sym); d.ident = id; // We mark all temporaries as final, as an indication that they will // be used only once. // d.mods.flags |= Flags.FINAL; // d.sym.flags_field |= Flags.FINAL; currentStatements.add(d); treeutils.copyEndPosition(d,expr); treeutils.copyEndPosition(id,expr); return id; } /** Creates a declaration for a new name and type initialized to a zero-equivalent literal. */ protected JCIdent newTempNull(DiagnosticPosition pos, Type type) { Name n = M.Name(Strings.tmpVarString + (++count)); // By having the owner be null, the BasicBlocker2 does not append any unique-ifying suffix - FIXME - does this affect RAC? JCVariableDecl d = treeutils.makeVarDef( type, n, esc ? null : methodDecl != null ? methodDecl.sym : classDecl.sym, // FIXME - actually sholdn't stuff at the class level be put in an initializer block? treeutils.makeZeroEquivalentLit(pos.getPreferredPosition(),type)); d.sym.pos = Position.NOPOS; // We mark all temporaries as final, as an indication that they will // be used only one. // d.mods.flags |= Flags.FINAL; // d.sym.flags_field |= Flags.FINAL; currentStatements.add(d); JCIdent id = treeutils.makeIdent(pos.getPreferredPosition(),d.sym); return id; } /** Returns true if the given symbol is specified as Helper or Function annotation */ public boolean isHelper(MethodSymbol symbol) { return attr.isHelper(symbol); } /** Returns true if the given symbol has a annotation */ public boolean isPure(MethodSymbol symbol) { return attr.isPureMethod(symbol); } /** Returns true if the given symbol has a Model annotation */ public boolean isModel(Symbol symbol) { return symbol.attribute(attr.tokenToAnnotationSymbol.get(JmlTokenKind.MODEL))!=null; // FIXME - need to get this from the spec } public boolean hasStatic(JCModifiers mods) { return (mods.flags & Flags.STATIC) != 0; } public boolean isOnlyComment(JCBlock block) { for (JCStatement st: block.stats) { if (st instanceof JmlStatementExpr && ((JmlStatementExpr)st).token == JmlTokenKind.COMMENT) continue; if (st instanceof JCBlock && isOnlyComment( (JCBlock)st )) continue; return false; } return true; } /** Creates a try statement that wraps the given block and catches any * RuntimeException; the catch block prints the given message; esc just * returns the given block */ public JCStatement wrapRuntimeException(DiagnosticPosition pos, JCBlock block, String message, @Nullable JCBlock catchStats) { if (!rac) return block; if (isOnlyComment(block)) return block; int p = pos.getPreferredPosition(); Name n = names.fromString(Strings.runtimeException); // FIXME - this does not need to be repeated every time JCVariableDecl vd = treeutils.makeVarDef(syms.runtimeExceptionType, n, methodDecl.sym, p); JCIdent ex = treeutils.makeIdent(p,vd.sym); JCExpression str = treeutils.makeStringLiteral(p,message); JCStatement st = methodCallUtilsStatement(pos,org.jmlspecs.utils.Utils.REPORT_EXCEPTION,str,ex); JCBlock bl = M.at(pos).Block(0, st != null ? ( catchStats != null ? List.<JCStatement>of(st,catchStats) : List.<JCStatement>of(st)) : ( catchStats != null ? List.<JCStatement>of(catchStats) : List.<JCStatement>nil())); JCCatch catcher = M.at(pos).Catch(vd,bl); // FIXME - this report needs a position of clause and name of method boolean quiet = utils.jmlverbose == 0; JCCatch catcher1; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchMethodError").type, names.fromString("noSuchMethodError"), methodDecl.sym, p); if (quiet) { catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); location = treeutils.makeNullLiteral(pos.getPreferredPosition()); // FIXME - really need to propagate the location of the call JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchMethod",id,location); catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } JCCatch catcher2; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchFieldError").type, names.fromString("noSuchFieldError"), methodDecl.sym, p); if (quiet) { catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); if (Utils.testingMode) location = treeutils.makeNullLiteral(pos.getPreferredPosition()); JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchField",id, location); catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } return M.at(pos).Try(block,List.<JCCatch>of(catcher,catcher1,catcher2),null); } /** Creates a try statement that wraps the given block and catches the * given exception; the declaration is the declaration of the variable to * use in the catch block; the catch block prints the given message and * executes the given stats. */ public JCStatement wrapException(DiagnosticPosition pos, JCBlock block, JCVariableDecl exceptionDecl, @Nullable JCBlock catchStats) { JCBlock bl = catchStats != null ? catchStats : M.at(pos).Block(0, List.<JCStatement>nil()); JCCatch catcher = M.at(pos).Catch(exceptionDecl,bl); return M.at(pos).Try(block,List.<JCCatch>of(catcher),null); } // protected void addInvariantsForAllFormals(DiagnosticPosition pos, MethodSymbol methodSym, TypeSymbol basecsym, JCIdent receiver, ListBuffer<JCStatement> stats, // boolean includeAxioms, boolean isPost, boolean assume, Label invariantLabel) { // // } // protected boolean isFinal(Symbol sym) { // return (sym.flags() & Flags.FINAL) != 0; // } /** Adds in assumptions or assertions for the non_nullity of visible fields * of the current and parent classes. */ public void addNonNullChecks(boolean assume, DiagnosticPosition pos, Type baseType, JCExpression receiver, boolean isConstructor) { boolean contextIsStatic = receiver == null; // && !methodDecl.sym.isConstructor(); //|| (self && utils.isJMLStatic(methodDecl.sym)); java.util.List<Type> parents = parents(baseType, false); for (Type ctype: parents) { if (!(ctype.tsym instanceof ClassSymbol)) continue; typevarMapping = typemapping(ctype, null, null); TypeSymbol csym = ctype.tsym; for (Symbol s : csym.getEnclosedElements()) { if (s instanceof VarSymbol) { boolean stat = utils.isJMLStatic(s); if (!utils.visible(classDecl.sym, csym, s.flags()/*, methodDecl.mods.flags*/)) continue; if (!stat && contextIsStatic) continue; if (!assume && isConstructor) continue; if (s.type.isPrimitive() || jmltypes.isJmlType(s.type)) continue; VarSymbol v = (VarSymbol)s; JCExpression e; if (receiver == null) e = M.at(pos).Ident(v); else e = M.at(pos).Select(receiver, v); JCExpression e1 = treeutils.makeNotNull(pos.getPreferredPosition(),e); JCExpression e2 = treeutils.makeNonNullDynamicTypeInEquality(pos, e, e.type); if (specs.isNonNull(v)) { e = treeutils.makeAnd(pos.getPreferredPosition(), e1, e2); } else { e = treeutils.makeImplies(pos.getPreferredPosition(), e1, e2); } if (assume) addAssume(pos,Label.POSSIBLY_NULL_VALUE, e, null,null); // FIXME - no associated position? else addAssert(pos,Label.POSSIBLY_NULL_VALUE, e, null,null); // FIXME - no associated position? } } } } /** Adds invariants, constraints and initially clauses from a given class and its parents. * This is used for pre and post conditions, in which case the invariants from a * meethod's own class (and its parents) are used; it is also used for other * relevant classes, such as the classes of method parameters. The value of * 'methodDecl' (and 'classDecl') is the method for which a logical encoding * is being constructed, but is not necessarily the callee class which is causing * the invariants to be loaded. * * @param pos the code position which occasioned needing the invariants * @param isConstructor whether the callee (or the method itself if there is no callee) is a contructor * @param isSuper true if the callee method is a super() call * @param basecsym the base TypeSymbol from which to take invariants (this may * or may not be the class containing the methodDecl) * @param receiver the THIS object to use when translating the invariants - * this is either the receiver for the original method call * or is the parameter or return value for which invariants * are being loaded. This parameter is null iff the base method * is static and not a constructor; if it is a constructor the * value is the object being produced by the constructor. * @param stats the statement list into which to add the invariants * @param isHelper whether the callee or base method is a helper method (whichever one the invariants are for) * @param prepost true if we are inserting invariants as pre or post conditions (rather than on behalf of a called procedure) * @param isPost whether we are adding clauses after (post) or before the method * @param assume whether clauses are assumed or asserted * @param invariantLabel the cause of needing the invariant */ protected void addInvariants(DiagnosticPosition pos, Type basetype, JCExpression receiver, ListBuffer<JCStatement> stats, boolean prepost, boolean isConstructor, boolean isSuper, boolean isHelper, boolean isPost, boolean assume, Label invariantLabel, Object ... invariantDescription) { TypeSymbol basecsym = basetype.tsym; if (basetype.isPrimitive()) return; if (!translatingJML) clearInvariants(); if (startInvariants(basecsym,pos)) return; // System.out.println("STARTING " + basecsym); java.util.List<Type> parents = parents(basetype, true); // boolean isConstructedObject = isConstructor && receiver.sym == currentThisId.sym; boolean self = basecsym == methodDecl.sym.owner; // true if we are inserting invariants for the base method, either as pre and post conditions or prior to calling a callee boolean contextIsStatic = receiver == null; // && !methodDecl.sym.isConstructor(); //|| (self && utils.isJMLStatic(methodDecl.sym)); // Iterate through parent classes and interfaces, assuming relevant axioms and invariants // These are checked prior to calling the callee ListBuffer<JCStatement> prevStats = currentStatements; JCIdent prevThisId = currentThisId; JCExpression prevThisExpr = currentThisExpr; Map<TypeSymbol,Type> savedTypevarMapping = typevarMapping; try { ListBuffer<JCStatement> staticStats = stats; // if (receiver instanceof JCFieldAccess) { // // FIXME - not sure about this patch (this branch) - needs testing // JCFieldAccess that = (JCFieldAccess)receiver; // currentThisExpr = currentThisId = treeutils.makeIdent(that.pos, that.name, that.sym); // } else if (receiver == null || receiver instanceof JCIdent) { { if (receiver instanceof JCIdent) currentThisId = (JCIdent)receiver; currentThisExpr = receiver; for (Type ctype: parents) { if (!(ctype.tsym instanceof ClassSymbol)) continue; typevarMapping = typemapping(ctype, null, null); ListBuffer<JCStatement> check = pushBlock(); ListBuffer<JCStatement> instanceStats = currentStatements; try { ClassSymbol csym = (ClassSymbol)ctype.tsym; JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) continue; // FIXME - why might this happen - see racnew.testElemtype & Cloneable if (prepost && !isPost && !methodDecl.sym.isConstructor()) { // Adding in invariant about final fields instanceStats.add(comment(pos,(assume? "Assume" : "Assert") + " final field invariants for " + csym,null)); JCExpression conj = null; //JCExpression staticconj = null; for (Symbol s : csym.getEnclosedElements()) { if (s instanceof VarSymbol) { if (contextIsStatic && !utils.isJMLStatic(s)) continue; // FIXME - check visibility VarSymbol v = (VarSymbol)s; Object o = v.getConstantValue(); if (o != null) { JCIdent id = treeutils.makeIdent(v.pos,v); JCLiteral val = treeutils.makeLit(v.pos,v.type,o); JCExpression e = treeutils.makeEquality(v.pos, id, val); if (utils.isJMLStatic(s)) { //staticconj = staticconj == null ? e : treeutils.makeAnd(v.pos, staticconj, e); } else { conj = conj == null ? e : treeutils.makeAnd(v.pos, conj, e); } } } } if (conj != null) { currentStatements = instanceStats; conj = convertJML(conj); if (assume) addAssume(pos,invariantLabel,conj); else addAssert(pos,invariantLabel,conj); } // if (staticconj != null) { // currentStatements = staticStats; // staticconj = convertJML(staticconj); // if (assume) addAssume(pos,invariantLabel,staticconj); // else addAssert(pos,invariantLabel,staticconj); // } } staticStats.add(comment(pos,(assume? "Assume" : "Assert") + " invariants for " + csym,null)); // Do the non_null fields // FIXME - not sure that we should exclue rac if (assume && !rac) for (Symbol s : csym.getEnclosedElements()) { if (s instanceof VarSymbol) { if (!utils.visible(classDecl.sym, csym, s.flags()/*, methodDecl.mods.flags*/)) continue; if (!utils.isJMLStatic(s) && contextIsStatic) continue; if (isConstructor && (!assume || basetype == ctype)) continue; VarSymbol v = (VarSymbol)s; Type vartype = v.type; JCExpression field; if (receiver == null) field = treeutils.makeIdent(pos,v); else field = M.at(pos).Select(receiver, v); if (!vartype.isPrimitive()) { JCExpression e = treeutils.makeNotNull(pos.getStartPosition(),field); // FIXME - position not right if (specs.isNonNull(v)) { if (assume) addAssume(pos,Label.POSSIBLY_NULL_VALUE, e, null,null); // FIXME - no associated position? else addAssert(pos,Label.POSSIBLY_NULL_VALUE, e, null,null); // FIXME - no associated position? } if (assume) { addAssume(pos, Label.IMPLICIT_ASSUME, treeutils.makeDynamicTypeInEquality(pos, field, vartype), null, null); // FIXME - associated position should be the declaration } } else { // FIXME - range constraints? } } } // Do the actual invariants for (JmlTypeClause clause : tspecs.clauses) { if (!utils.visible(classDecl.sym, csym, clause.modifiers.flags/*, methodDecl.mods.flags*/)) continue; JmlTypeClauseExpr t; DiagnosticPosition cpos = clause; boolean clauseIsStatic = utils.isJMLStatic(clause.modifiers,csym); currentStatements = clauseIsStatic? staticStats : instanceStats; //JavaFileObject prevSource = log.useSource(clause.source()); try { // FIXME - guard against the receiver being null for non-static invariants switch (clause.token) { // invariants on both pre and post for all classes of parameters and return value // Pre and post conditions: // helper - no invariants for class of method // non-helper constructor - invariants only on post // non-helper method - invariants on pre and post // Calling a method - caller invariants // same as pre and postconditions, except // when a constructor is calling super(), no invariants of containing class are assumed in post // Calling a method - callee invariants // callee is helper - no invariants in pre or post for class containing method // callee is super() - invariants of super class on post // callee is constructor - invariants on post // callee is method - invariants on pre and post case INVARIANT: { if (contextIsStatic && !clauseIsStatic) break; if (isHelper) break; if (isSuper && !isPost) break; boolean doit = false; if (!isConstructor || isPost) doit = true; // pre and postcondition case if (isConstructor && clauseIsStatic) doit = true; if (doit) { t = (JmlTypeClauseExpr)convertCopy(clause); // FIXME - why copy the clause addTraceableComment(t.expression,clause.toString()); JCExpression e = convertJML(t.expression,treeutils.trueLit,isPost); // FIXME - be nice to record where called from, not just the location of the invariant declaration if (assume) addAssume(pos,invariantLabel, e, cpos,clause.source, invariantDescription); else addAssert(pos,invariantLabel, e, cpos,clause.source, invariantDescription); // JavaFileObject source = log.useSource(clause.source); // if (assume) addAssume(cpos,invariantLabel, // e, // cpos,clause.source, invariantDescription); // else addAssert(cpos,invariantLabel, // e, // invariantDescription); // log.useSource(source); } break; } default: // Skip } } catch (NoModelMethod e) { // log.error(clause.pos, "jml.message", e.getMessage()); } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ", e, clause.source()); } finally { //log.useSource(prevSource); } } } finally { currentStatements = instanceStats; JCBlock bl = popBlock(0,pos,check); if (!onlyComments(bl.stats)) { if (contextIsStatic) { staticStats.add(bl); } else { JCExpression ex = treeutils.makeNeqObject(pos.getPreferredPosition(),receiver,treeutils.nullLit); JCStatement st = M.at(pos).If(ex, bl, null); staticStats.add(st); } } } } // } else { // notImplemented(receiver, "receiver of class " + (receiver == null ? "-null-" : receiver.getClass().getName())); } } finally { endInvariants(basecsym); if (!translatingJML) clearInvariants(); // System.out.println("ENDING " + basecsym + " " + inProcessInvariants.contains(basecsym) + " " + completedInvariants.contains(basecsym)); currentStatements = prevStats; currentThisId = prevThisId; currentThisExpr = prevThisExpr; typevarMapping = savedTypevarMapping; } } protected void assertInvariants(JCExpression expr, JCExpression currentThis) { JCExpression saved = currentThisExpr; currentThisExpr = currentThis; ListBuffer<JCStatement> check10 = pushBlock(); try { for (JmlTypeClause t: specs.getSpecs((ClassSymbol)expr.type.tsym).clauses) { if (t.token != JmlTokenKind.INVARIANT) continue; JavaFileObject prev = log.useSource(t.source); try { JCExpression e = convertJML(convertCopy((JmlTypeClauseExpr)t).expression); // FIXME - why copy the caluse addAssert(t,Label.INVARIANT,e); // FIXME - CHnage the position to point to end point of method, and associated position to invariant } finally { log.useSource(prev); } } } finally { JCBlock bl = popBlock(0L,expr,check10); if (!bl.stats.isEmpty()) { JCExpression nn = treeutils.makeNeqObject(expr.pos, expr, treeutils.nullLit); JCStatement st = M.at(expr.pos).If(nn, bl, null); addStat(st); } currentThisExpr = saved; } } protected void addRecInvariants(boolean assume, JCVariableDecl d, JCExpression currentThis) { JCExpression saved = currentThisExpr; currentThisExpr = currentThis; for (ClassSymbol csym: utils.parents(d.type.tsym, false)) { if (esc) addNullnessAndTypeConditionsForFields(csym,false); addInvariants(assume,d,csym,currentThis); } currentThisExpr = saved; } protected void addInvariants(boolean assume, JCVariableDecl d, ClassSymbol csym, JCExpression currentThis) { JCExpression saved = currentThisExpr; currentThisExpr = currentThis; pushBlock(); try { for (JmlTypeClause t: specs.getSpecs(csym).clauses) { if (t.token != JmlTokenKind.INVARIANT) continue; JavaFileObject prev = log.useSource(t.source); try { JCExpression e = convertJML( convertCopy((JmlTypeClauseExpr)t).expression); // FIXME - really need the convertCopy? if (assume) addAssume(t,Label.INVARIANT,e); else addAssert(t,Label.INVARIANT,e); } catch (NoModelMethod ex) { // Just skip } finally { log.useSource(prev); } } Scope cs = csym.members(); if (esc) do { for (Symbol s: cs.getElements()) { if (!(s instanceof VarSymbol)) continue; addNullnessAllocationTypeCondition2(d,s,false); } cs = cs.next; } while (cs != null) ; } finally { JCBlock bl = popBlock(0L,d); if (!bl.stats.isEmpty()) { if (utils.isJMLStatic(d.sym)) { addStat(bl); } else { JCExpression nn = treeutils.makeNeqObject(d.pos, convertCopy(currentThisExpr), treeutils.nullLit); JCStatement st = M.at(d.pos).If(nn, bl, null); addStat(st); } } currentThisExpr = saved; } } protected void addConstraintInitiallyChecks(DiagnosticPosition pos, TypeSymbol basecsym, JCExpression receiver, ListBuffer<JCStatement> stats, boolean prepost, boolean isConstructor, boolean isSuper, boolean isHelper, boolean isPost, boolean assume, Label invariantLabel, Object ... invariantDescription) { if (basecsym.type.isPrimitive()) return; // if (!translatingJML) clearInvariants(); java.util.List<ClassSymbol> parents = utils.parents(basecsym, false); // boolean isConstructedObject = isConstructor && receiver.sym == currentThisId.sym; boolean self = basecsym == methodDecl.sym.owner; // true if we are inserting invariants for the base method, either as pre and post conditions or prior to calling a callee boolean contextIsStatic = receiver == null ; //|| (self && utils.isJMLStatic(methodDecl.sym)); // Iterate through parent classes and interfaces, assuming relevant axioms and invariants // These are checked prior to calling the callee ListBuffer<JCStatement> prevStats = currentStatements; JCIdent prevThisId = currentThisId; JCExpression prevThisExpr = currentThisExpr; try { ListBuffer<JCStatement> staticStats = stats; currentThisId = receiver instanceof JCIdent ? (JCIdent)receiver : null; currentThisExpr = receiver; for (ClassSymbol csym: parents) { ListBuffer<JCStatement> check = pushBlock(); ListBuffer<JCStatement> instanceStats = currentStatements; boolean pv = checkAccessEnabled; checkAccessEnabled = false; // Do not check access in JML clauses try { JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) continue; // FIXME - why might this happen - see racnew.testElemtype & Cloneable staticStats.add(comment(pos,(assume? "Assume" : "Assert") + " constraints for " + csym,null)); for (JmlTypeClause clause : tspecs.clauses) { if (!utils.visible(classDecl.sym, csym, clause.modifiers.flags/*, methodDecl.mods.flags*/)) continue; JmlTypeClauseExpr t; DiagnosticPosition cpos = clause; boolean clauseIsStatic = utils.isJMLStatic(clause.modifiers,csym); currentStatements = clauseIsStatic? staticStats : instanceStats; try { // FIXME - guard against the receiver being null for non-static invariants switch (clause.token) { case CONSTRAINT: if (!isPost) break; if ((!isConstructor && !contextIsStatic) || clauseIsStatic) { JmlTypeClauseConstraint tt = (JmlTypeClauseConstraint)clause; if (tt.sigs != null) { boolean found = false; for (JmlMethodSig sig : tt.sigs) { if (sig.methodSymbol == methodDecl.sym) { found = true; break; } } if (found == tt.notlist) break; } pushBlock(); try { addTraceableComment(tt.expression,clause.toString()); JCExpression e = convertJML(tt.expression); if (assume) addAssume(pos,Label.CONSTRAINT, e, cpos, clause.source); else addAssert(pos,Label.CONSTRAINT, e, cpos, clause.source); } finally { addStat( popBlock(0,clause)); } } break; case INITIALLY: if (isPost && isConstructor) { pushBlock(); try { t = (JmlTypeClauseExpr)clause; addTraceableComment(t.expression,clause.toString()); JCExpression e = convertJML(t.expression); if (assume) addAssume(pos,Label.INITIALLY, e, cpos,clause.source); else addAssert(pos,Label.INITIALLY, e, cpos,clause.source); } finally { addStat( popBlock(0,cpos)); } } break; default: // Skip } } catch (NoModelMethod e) { // FIXME - what to do. } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ", e, clause.source()); } } } finally { checkAccessEnabled = pv; currentStatements = instanceStats; JCBlock bl = popBlock(0,pos,check); if (!onlyComments(bl.stats)) { if (contextIsStatic) { staticStats.add(bl); } else { JCExpression ex = treeutils.makeNeqObject(pos.getPreferredPosition(),receiver,treeutils.nullLit); JCStatement st = M.at(pos).If(ex, bl, null); staticStats.add(st); } } } } } finally { currentStatements = prevStats; currentThisId = prevThisId; currentThisExpr = prevThisExpr; } } protected void addAxioms(DiagnosticPosition pos, TypeSymbol basecsym, ListBuffer<JCStatement> stats, JCExpression receiver) { if (rac) return; java.util.List<ClassSymbol> parents = utils.parents(basecsym, true); // Iterate through parent classes and interfaces, assuming relevant axioms ListBuffer<JCStatement> prevStats = currentStatements; JCIdent prevThisId = currentThisId; JCExpression prevThisExpr = currentThisExpr; boolean pv = checkAccessEnabled; checkAccessEnabled = false; // Do not check access in JML clauses try { currentStatements = stats; currentThisId = (JCIdent)receiver; currentThisExpr = receiver; for (ClassSymbol csym: parents) { if (!addAxioms(heapCount,csym)) continue; JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) continue; // FIXME - why might this happen - see racnew.testElemtype & Cloneable for (JmlTypeClause clause : tspecs.clauses) { DiagnosticPosition cpos = clause; try { // FIXME - guard against the receiver being null for non-static invariants switch (clause.token) { case AXIOM: // Axioms are included only when assuming the preconditions // for a method body - both the main procedure and // any axioms for the classes of the parameters and return values if (rac) { notImplemented(clause, "axiom clause", clause.source()); } else if (esc) { addStat(comment(clause)); JmlTypeClauseExpr t = (JmlTypeClauseExpr)clause; JCExpression e = convertJML(t.expression); addAssume(pos,Label.AXIOM, e, cpos,clause.source); } break; default: // Skip } } catch (NoModelMethod e) { // FIXME - what to do. } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ", e, clause.source()); } } } } finally { currentStatements = prevStats; currentThisId = prevThisId; currentThisExpr = prevThisExpr; checkAccessEnabled = false; } } public void addRepresentsAxioms(TypeSymbol clsym, Symbol varsym, JCTree that, JCExpression translatedSelector) { reps.add(0,varsym); boolean pv = checkAccessEnabled; checkAccessEnabled = false; // Do not check access in JML clauses try { if (rac) { Name mmName = names.fromString(Strings.modelFieldMethodPrefix + varsym.toString()); java.util.List<Type> p = parents(clsym.type, true); ListIterator<Type> iter = p.listIterator(p.size()); while (iter.hasPrevious()) { JmlSpecs.TypeSpecs tyspecs = specs.getSpecs((ClassSymbol)iter.previous().tsym); for (JmlTypeClauseDecl x: tyspecs.modelFieldMethods) { if (x.decl instanceof JmlMethodDecl) { JmlMethodDecl md = (JmlMethodDecl)x.decl; // THe DEFAULT Flag is used to indicate that the method is just the place-holder method // created in JmlMemberEnter. It should be ignored as it is not a user supplied method. if (md.name == mmName ) { // && (md.mods.flags & Flags.DEFAULT) == 0) { JCMethodInvocation app = treeutils.makeMethodInvocation(that,translatedSelector,md.sym); result = eresult = app; treeutils.copyEndPosition(eresult, that); return; } } } } // String optvalue = JmlOption.value(context, JmlOption.MODEL_FIELD_NO_REP); // if ("zero".equals(optvalue)) { result = eresult = treeutils.makeZeroEquivalentLit(that.pos, varsym.type); // } else if ("ignore".equals(optvalue)) { // throw new NoModelMethod("No represents clause for model field " + varsym); // } else { Env<AttrContext> env = JmlEnter.instance(context).getEnv(clsym); // We don't warn if the problem is that we just have a binary class and consequently no implementations of model fields // FIXME - need some tests for these options - not sure the binary ones have any effect here??? if (env != null && ((JmlCompilationUnit)env.toplevel).mode != JmlCompilationUnit.SPEC_FOR_BINARY) { String opt = JmlOption.value(context,JmlOption.RAC_MISSING_MODEL_FIELD_REP_SOURCE); if ("skip".equals(opt)) { throw new NoModelMethod("No represents clause for model field " + varsym); } else { // already set the eresult if ("warn".equals(opt)) log.warning(that.pos, "jml.no.model.method.ignore", varsym.owner.getQualifiedName().toString() + "." + varsym.toString()); } } else { String opt = JmlOption.value(context,JmlOption.RAC_MISSING_MODEL_FIELD_REP_BINARY); if ("skip".equals(opt)) { throw new NoModelMethod("No represents clause for model field " + varsym); } else { // already set the eresult if ("warn".equals(opt)) log.warning(that.pos, "jml.no.model.method.ignore", varsym.owner.getQualifiedName().toString() + "." + varsym.toString()); } } // } return; } if (esc) { java.util.List<Type> p = parents(clsym.type, true); ListIterator<Type> iter = p.listIterator(p.size()); while (iter.hasPrevious()) { TypeSymbol ty = iter.previous().tsym; if (!(ty instanceof ClassSymbol)) continue; // FIXME - could be a TypeVariable TypeSpecs tspecs = specs.getSpecs((ClassSymbol)ty); for (JmlTypeClause tc : tspecs.clauses) { if (tc.token == JmlTokenKind.REPRESENTS) { JmlTypeClauseRepresents rep = (JmlTypeClauseRepresents)tc; if (rep.ident instanceof JCIdent) { if (((JCIdent)rep.ident).sym != varsym) continue; if (rep.suchThat){ addAssume(that,Label.IMPLICIT_ASSUME, convertExpr(rep.expression)); result = eresult = treeutils.makeSelect(that.pos, translatedSelector, varsym); } else { // FIXME - This does not work if the model variable is used within a pure method spec, because it ends up in the body of a constructed quantifier, but localVariables is not used // if (localVariables.isEmpty()) { // addAssumeEqual(that,Label.IMPLICIT_ASSUME, // convertExpr(rep.ident), // convertExpr(rep.expression)); // } else { JCExpression prev = currentThisExpr; currentThisExpr = translatedSelector; try { //JCIdent id = newTemp(convertExpr(rep.expression)); // New variable is initialized - no assume statement needed // JCExpression e = treeutils.makeEquality(rep.pos,id,convertExpr(rep.expression)); // addAssume(that,Label.IMPLICIT_ASSUME,e); //result = eresult = id; result = eresult = convertExpr(rep.expression); } finally { currentThisExpr = prev; } } } else { log.error("jml.internal","Kind of represents ID that is not implemented: " + rep.ident.getClass()); } return; } } result = eresult = treeutils.makeSelect(that.pos, translatedSelector, varsym); return; } } } finally { reps.remove(varsym); checkAccessEnabled = pv; } result = eresult = treeutils.makeSelect(that.pos, translatedSelector, varsym); } protected void addInvariantsForVar(JCExpression thisExpr) { assertInvariants(thisExpr,thisExpr); } /** Returns true iff the declaration is explicitly or implicitly non_null */ protected boolean addNullnessAllocationTypeCondition2(JCVariableDecl d, Symbol sym, boolean instanceBeingConstructed) { boolean isNonNull = true; Symbol owner = sym.owner; if (owner instanceof MethodSymbol) owner = owner.owner; if (!sym.type.isPrimitive() && !jmltypes.isJmlType(sym.type)) { isNonNull = specs.isNonNull(sym) ; } return addNullnessAllocationTypeCondition(d, sym, isNonNull, instanceBeingConstructed); } protected boolean addNullnessAllocationTypeCondition(DiagnosticPosition pos, Symbol sym, boolean instanceBeingConstructed) { boolean isNonNull = true; Symbol owner = sym.owner; if (owner instanceof MethodSymbol) owner = owner.owner; if (!sym.type.isPrimitive() && !jmltypes.isJmlType(sym.type)) { isNonNull = specs.isNonNull(sym, (Symbol.ClassSymbol)owner) ; } return addNullnessAllocationTypeCondition(pos,sym,isNonNull,instanceBeingConstructed); } /** Returns true iff the declaration is explicitly or implicitly non_null */ protected boolean addNullnessAllocationTypeCondition(DiagnosticPosition pos, Symbol sym, boolean isNonNull, boolean instanceBeingConstructed) { int p = pos.getPreferredPosition(); JCExpression id; if (sym.owner instanceof MethodSymbol) { // Local variable id = treeutils.makeIdent(p, sym); } else { // Field id = treeutils.makeSelect(p, currentThisExpr, sym); } return addNullnessAllocationTypeConditionId(id, pos, sym, isNonNull, instanceBeingConstructed); } /** Returns true iff the declaration is explicitly or implicitly non_null */ protected boolean addNullnessAllocationTypeConditionId(JCExpression id, DiagnosticPosition pos, Symbol sym, boolean isNonNull, boolean instanceBeingConstructed) { int p = pos.getPreferredPosition(); boolean nnull = true; if (sym.type.toString().equals("\\bigint")) Utils.stop(); if (!jmltypes.isJmlType(sym.type) && !sym.type.isPrimitive()) { // e2 : id.isAlloc //JCExpression e2 = treeutils.makeSelect(p, convertCopy(id), isAllocSym); JCExpression e2 = treeutils.makeBinary(p, JCTree.Tag.LE, treeutils.makeSelect(p, convertCopy(id), allocSym), treeutils.makeIntLiteral(p, 0)); Symbol owner = sym.owner; if (owner instanceof MethodSymbol) owner = owner.owner; boolean nn = isNonNull && !instanceBeingConstructed; nnull = nn; if (nn) { // assume id != null addAssume(pos,Label.NULL_CHECK,treeutils.makeNotNull(p, convertCopy(id))); // assume id.isAlloc addAssume(pos,Label.IMPLICIT_ASSUME,e2); } else { JCExpression e1 = treeutils.makeEqObject(p,id, treeutils.makeNullLiteral(p)); // assume id == null || id.isAlloc addAssume(pos,Label.IMPLICIT_ASSUME,treeutils.makeOr(p, e1, e2)); } // assume type information // e3: id == null || ( \typeof(id) <: \type(d.type) && id instanceof \erasure('d.type'))) JCExpression e3; if (sym.type.isPrimitive() || (sym.type.getTag() == TypeTag.ARRAY && ((Type.ArrayType)sym.type).getComponentType().isPrimitive() )) { e3 = treeutils.makeDynamicTypeEquality(pos, convertCopy(id), sym.type); JCStatement s = addAssume(pos,Label.IMPLICIT_ASSUME,e3); addTraceableComment(s); } else if (sym.type.getTag() == TypeTag.ARRAY ) { Type compType = ((Type.ArrayType)sym.type).getComponentType(); if (compType.isPrimitive()) { e3 = treeutils.makeDynamicTypeEquality(pos, convertCopy(id), sym.type); } else { e3 = treeutils.makeDynamicTypeInEquality(pos, convertCopy(id), sym.type); } JCStatement s = addAssume(pos,Label.IMPLICIT_ASSUME,e3); addTraceableComment(s); // FIXME - this should be in a recursive loop. Does it need nullness and allocation checks? JCExpression typeofId = treeutils.makeTypeof(id); if (compType.isPrimitive()) { e3 = treeutils.makeDynamicTypeEquality(pos, convertCopy(typeofId), compType); } else { e3 = treeutils.makeDynamicTypeEquality(pos, convertCopy(typeofId), compType); } } else { e3 = null; for (Type t: parents(sym.type.tsym.type, false)) { // OK - no enclsoing types JCExpression ee3 = treeutils.makeDynamicTypeInEquality(pos, convertCopy(id), t); e3 = e3 == null ? ee3 : treeutils.makeAnd(ee3.pos, e3, ee3); } JCStatement s = addAssume(pos,Label.IMPLICIT_ASSUME,e3); addTraceableComment(s); } } else { TypeTag tag = sym.type.getTag(); JCExpression lo = null,hi=null; if (tag == TypeTag.INT) { lo = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol, treeutils.makeLit(p,syms.intType,Integer.MIN_VALUE),convertCopy(id)); hi = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol,convertCopy(id), treeutils.makeLit(p,syms.intType,Integer.MAX_VALUE)); } else if (tag == TypeTag.LONG) { lo = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.longleSymbol, treeutils.makeLit(p,syms.intType,Long.MIN_VALUE),convertCopy(id)); hi = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.longleSymbol,convertCopy(id), treeutils.makeLit(p,syms.intType,Long.MAX_VALUE)); } else if (tag == TypeTag.SHORT) { lo = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol, treeutils.makeLit(p,syms.intType,(int)Short.MIN_VALUE),convertCopy(id)); hi = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol,convertCopy(id), treeutils.makeLit(p,syms.intType,(int)Short.MAX_VALUE)); } else if (tag == TypeTag.BYTE) { lo = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol, treeutils.makeLit(p,syms.intType,(int)Byte.MIN_VALUE),convertCopy(id)); hi = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol,convertCopy(id), treeutils.makeLit(p,syms.intType,(int)Byte.MAX_VALUE)); } else if (tag == TypeTag.CHAR) { lo = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol, treeutils.makeLit(p,syms.intType,(int)Character.MIN_VALUE),convertCopy(id)); hi = treeutils.makeBinary(p,JCTree.Tag.LE,treeutils.intleSymbol,convertCopy(id), treeutils.makeLit(p,syms.intType,(int)Character.MAX_VALUE)); } if (lo != null) { JCStatement s = addAssume(pos,Label.IMPLICIT_ASSUME,treeutils.makeAnd(p,lo,hi)); addTraceableComment(s); } } return nnull; } /** Add all axioms from listed classes and their parents */ public void addForClasses(java.util.Collection<ClassSymbol> classes, IClassOp op) { Set<ClassSymbol> done = new HashSet<ClassSymbol>(); for (ClassSymbol csym: classes) { for (ClassSymbol cs: utils.parents(csym, true)) { if (done.add(cs)) op.classOp(cs); } } done.clear(); } boolean addingAxioms = false; /** Add all axioms from this specific class */ protected void addClassAxioms(ClassSymbol csym) { if (!addAxioms(heapCount,csym)) return; boolean prevAddingAxioms = addingAxioms; addingAxioms = true; try { JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) return; // FIXME - why might this happen - see racnew.testElemtype & Cloneable for (JmlTypeClause clause : tspecs.clauses) { DiagnosticPosition cpos = clause; try { // Note: axioms have no modifiers but nonetheless must always be effectively static if (clause.token == JmlTokenKind.AXIOM) { addStat(comment(clause)); JmlTypeClauseExpr t = (JmlTypeClauseExpr)clause; JCExpression e = convertJML(t.expression); addAssume(cpos,Label.AXIOM, e, cpos,clause.source); } } catch (NoModelMethod e) { // FIXME - what to do. } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ", e, clause.source()); } catch (Exception e) { Utils.stop(); // FIXME - what to do - } } } finally { addingAxioms = prevAddingAxioms; } } /** Add all constant final static fields from this specific class */ protected void addFinalStaticFields(ClassSymbol csym) { Env<AttrContext> enva = Enter.instance(context).getEnv(csym); if (enva == null) return; JmlClassDecl decl = (JmlClassDecl)enva.tree; if (decl == null) return; for (JCTree def : decl.defs) { if (def instanceof JmlVariableDecl) { JmlVariableDecl vd = (JmlVariableDecl)def; if (!utils.jmlvisible(vd.sym, methodDecl.sym.owner, csym, vd.mods.flags, methodDecl.mods.flags)) continue; if ((vd.sym.flags() & (Flags.FINAL|Flags.STATIC)) == (Flags.FINAL|Flags.STATIC)) { JCExpression e = vd.init; if (e != null) { Object constValue = e.type.constValue(); if (constValue == null) continue; JCExpression lit = treeutils.makeLit(e.pos, e.type, constValue); lit = addImplicitConversion(e,vd.type,lit); addStat(treeutils.makeVariableDecl(vd.sym, lit)); // Note - with the above declaration and initialization, BasicBlocker2 adds an assumption // JCExpression expr = treeutils.makeEquality(vd.pos, treeutils.makeIdent(vd.pos,vd.sym), lit); // addAssume(vd,Label.IMPLICIT_ASSUME,convertExpr(expr)); // vd.init = lit; // vd.sym.owner = null; // This null makes the identifier not subject to havoc // addStat(vd); } } } } JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) return; // FIXME - why might this happen - see racnew.testElemtype & Cloneable for (JmlTypeClause clause : tspecs.decls) { try { if (!(clause instanceof JmlTypeClauseDecl)) continue; JmlTypeClauseDecl tdecl = (JmlTypeClauseDecl)clause; if (!(tdecl.decl instanceof JmlVariableDecl)) continue; JmlVariableDecl vdecl = (JmlVariableDecl)tdecl.decl; if (vdecl.init == null) continue; if (!utils.jmlvisible(null, methodDecl.sym.owner, csym, vdecl.mods.flags, methodDecl.mods.flags)) continue; if ((vdecl.sym.flags() & (Flags.FINAL | Flags.STATIC)) != (Flags.FINAL|Flags.STATIC)) continue; Object constValue = vdecl.init.type.constValue(); if (constValue == null) continue; addStat(vdecl); } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ", e, clause.source()); } } } protected void assumeStaticInvariants(ClassSymbol csym) { if (startInvariants(csym,methodDecl)) return; JmlSpecs.TypeSpecs tspecs = specs.get(csym); if (tspecs == null) return; // FIXME - why might this happen - see racnew.testElemtype & Cloneable for (JmlTypeClause t : tspecs.clauses) { if (t.token != JmlTokenKind.INVARIANT) continue; if (!utils.isJMLStatic(t.modifiers,csym)) continue; if (!utils.jmlvisible(null, methodDecl.sym.owner, csym, t.modifiers.flags, methodDecl.mods.flags)) continue; addAssume(methodDecl,Label.INVARIANT_ENTRANCE, convertJML( convertCopy((JmlTypeClauseExpr)t).expression), // FIXME - really need the convertCopy? t,t.source(), utils.qualifiedMethodSig(methodDecl.sym)); } endInvariants(csym); } public interface IClassOp { public void classOp(ClassSymbol csym); } public final IClassOp axiomAdder = new IClassOp() { public void classOp(ClassSymbol cs) { addClassAxioms(cs); }}; public final IClassOp finalStaticFieldAdder = new IClassOp() { public void classOp(ClassSymbol cs) { addFinalStaticFields(cs); }}; public final IClassOp staticInvariantAdder = new IClassOp() { public void classOp(ClassSymbol cs) { assumeStaticInvariants(cs); }}; /** Computes and adds checks for all the pre and postcondition clauses. */ // FIXME - review this protected void addPreConditions(ListBuffer<JCStatement> initialStats) { JCMethodDecl methodDecl = this.methodDecl; int pos = methodDecl.pos; boolean isConstructor = methodDecl.sym.isConstructor(); ListBuffer<JCStatement> savedCurrentStatements = currentStatements; currentStatements = initialStats; int preheapcount = heapCount++; int postheapcount = heapCount++; int savedheapcount = heapCount; heapCount = preheapcount; /* Declare new variables, initialized to the values of the formal * parameters, so that they can be referred to in postconditions and other invariant checks. */ if (!methodDecl.params.isEmpty()) addStat(comment(methodDecl,"Declare pre-state value of formals",null)); for (JCVariableDecl d: methodDecl.params) { JCVariableDecl dd = treeutils.makeVarDef(d.type,M.Name(Strings.formalPrefix+d.name.toString()), d.sym.owner, M.Ident(d.sym)); dd.pos = dd.sym.pos = d.sym.pos; preparams.put(d.sym,dd); if (esc) dd.sym.owner = null; // FIXME - can get these straight from the labelmaps - do we need this? addStat(dd); // Declare allocation time of formals if they are not null if (esc && !d.type.isPrimitive()) { JCIdent id = treeutils.makeIdent(d.pos, d.sym); JCExpression nn = treeutils.makeEqNull(d.pos,id); JCExpression fa = M.at(d.pos).Select(id, allocSym); fa = treeutils.makeBinary(d,JCTree.Tag.LE, fa, treeutils.makeIntLiteral(d,0)); fa = treeutils.makeOr(d.pos, nn, fa); addStat(treeutils.makeAssume(d, Label.IMPLICIT_ASSUME, fa )); } } // Collect all classes that are mentioned in the method ClassCollector collector = ClassCollector.collect(this.classDecl,this.methodDecl); // Assume all axioms of classes mentioned in the target method if (esc) { addStat(comment(methodDecl,"Assume axioms",null)); addForClasses(collector.classes, axiomAdder); addStat(comment(methodDecl,"Assume static final constant fields",null)); addForClasses(collector.classes, finalStaticFieldAdder); //addStat(comment(methodDecl,"Static initialization")); //if (isConstructor) addStaticInitialization((ClassSymbol)methodDecl.sym.owner); } // FIXME - include for rac also? if (esc) { // Add nullness andtype conditions for fields and inherited fields addStat(comment(methodDecl,"Assume field type, allocation, and nullness",null)); addNullnessAndTypeConditionsForInheritedFields(classDecl.sym, isConstructor); // For the parameters of the method addStat(comment(methodDecl,"Assume parameter type, allocation, and nullness",null)); boolean varargs = (methodDecl.sym.flags() & Flags.VARARGS) != 0; boolean isNonNull = true; for (JCVariableDecl d: methodDecl.params) { isNonNull = addNullnessAllocationTypeCondition2(d,d.sym,false); } if (varargs && !isNonNull) { // isNonNull is the nullness of the last parameter, so the varargs parameter JCVariableDecl d = methodDecl.params.last(); JCIdent id = treeutils.makeIdent(d.pos,d.sym); JCExpression nnex = treeutils.makeNeqObject(d.pos,id,treeutils.nullLit); addAssume(d.pos(),Label.IMPLICIT_ASSUME,nnex); } // FIXME - for the isConstructor case, it should be newly allocated if (isConstructor) { } else if (!utils.isJMLStatic(methodDecl.sym)){ // Assume that 'this' is allocated JCFieldAccess fa = treeutils.makeSelect(pos, convertCopy(currentThisExpr), isAllocSym); addAssume(methodDecl,Label.IMPLICIT_ASSUME,fa); } } // Assume invariants for the class of each field of the owning class JCExpression savedThis = currentThisExpr; for (JCTree dd: classDecl.defs) { if (!(dd instanceof JCVariableDecl)) continue; JCVariableDecl d = (JCVariableDecl)dd; if (d.sym.type.isPrimitive()) continue; if (!utils.isJMLStatic(d.sym) && utils.isJMLStatic(methodDecl.sym)) continue; if (isHelper(methodDecl.sym) && d.sym.type.tsym == methodDecl.sym.owner.type.tsym) continue; boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { JCExpression fa = convertJML(treeutils.makeIdent(d.pos, d.sym)); addStat(comment(dd,"Adding invariants for " + d.sym,null)); addRecInvariants(true,d,fa); } finally { checkAccessEnabled = pv; } } currentThisExpr = savedThis; for (JmlTypeClauseDecl dd: specs.get(classDecl.sym).decls) { JCTree tt = dd.decl; if (!(tt instanceof JCVariableDecl)) continue; JCVariableDecl d = (JCVariableDecl)tt; if (d.sym.type.isPrimitive()) continue; if (!utils.isJMLStatic(d.sym) && utils.isJMLStatic(methodDecl.sym)) continue; JCExpression fa = convertJML(treeutils.makeIdent(d.pos, d.sym)); addStat(comment(dd,"Adding invariants for " + d.sym,null)); addRecInvariants(true,d,fa); } currentThisExpr = savedThis; // We collect the precondition evaluation in a separate list so that // we can wrap the evaluations in a try-catch block, in case there are // exceptions thrown ListBuffer<JCStatement> preStats = new ListBuffer<JCStatement>(); currentStatements = preStats; // Construct a condition, to be used later, that the method has not thrown an exception DiagnosticPosition methodPos = methodDecl; JCExpression noException = treeutils.makeEqObject(methodPos.getPreferredPosition(), treeutils.makeIdent(methodPos.getPreferredPosition(), exceptionSym), treeutils.nullLit); // FIXME - what if the owning class is a TypeSymbol because it is a TypeVar TypeSymbol owner = (TypeSymbol)methodDecl.sym.owner; JCExpression receiver = utils.isJMLStatic(methodDecl.sym) ? null : currentThisExpr; // Assuming invariants addInvariants(methodDecl,owner.type,receiver,currentStatements,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),false,true,Label.INVARIANT_ENTRANCE, utils.qualifiedMethodSig(methodDecl.sym) ); // Assume invariants for the class of each parameter for (JCVariableDecl v: methodDecl.params) { if (v.type.isPrimitive()) continue; JCVariableDecl d = preparams.get(v.sym); if (isHelper(methodDecl.sym) && d.sym.type.tsym == methodDecl.sym.owner.type.tsym) continue; if (owner.type.tsym == v.type.tsym && methodDecl.sym.isConstructor()) { JCIdent id = treeutils.makeIdent(v.pos,d.sym); addAssume(v,Label.IMPLICIT_ASSUME, treeutils.makeNeqObject(v.pos, id, currentThisExpr)); } JCIdent id = treeutils.makeIdent(v.pos,d.sym); addStat(comment(v,"Adding invariants for " + v.sym,null)); addInvariants(v,v.type,id,currentStatements,true,false,false,false,false,true,Label.INVARIANT_ENTRANCE, utils.qualifiedMethodSig(methodDecl.sym) + " (parameter " + v.name + ")"); } // Invariants for fields savedThis = currentThisExpr; currentThisExpr = savedThis; JCExpression combinedPrecondition = null; JavaFileObject combinedPreconditionSource = null; addStat( comment(methodDecl,"Assume Preconditions",null)); // Iterate over all methods that methodDecl overrides, collecting specs boolean sawSomeSpecs = false; for (MethodSymbol msym: utils.parents(methodDecl.sym)) { if (msym.params == null) continue; // FIXME - we should do something better? or does this mean binary with no specs? JmlMethodSpecs denestedSpecs = JmlSpecs.instance(context).getDenestedSpecs(msym); // Set up the map from parameter symbol of the overridden method to // corresponding parameter of the target method. // We need this even if names have not changed, because the parameters // will have been attributed with different symbols. if (denestedSpecs.decl != null) { Iterator<JCVariableDecl> iter = denestedSpecs.decl.params.iterator(); paramActuals = new HashMap<Object,JCExpression>(); for (JCVariableDecl dp: methodDecl.params) { JCVariableDecl newdecl = iter.next(); paramActuals.put(newdecl.sym,treeutils.makeIdent(dp.pos, dp.sym)); } } else { // FIXME - why should denestedSpecs ever not have a declaration if there are any specs to use Iterator<VarSymbol> iter = msym.params.iterator(); paramActuals = new HashMap<Object,JCExpression>(); for (JCVariableDecl dp: methodDecl.params) { VarSymbol newsym = iter.next(); paramActuals.put(newsym,treeutils.makeIdent(dp.pos, dp.sym)); } } heapCount = preheapcount; for (JmlSpecificationCase scase : denestedSpecs.cases) { sawSomeSpecs = true; if (!utils.visible(classDecl.sym, msym.owner, scase.modifiers.flags/*, methodDecl.mods.flags*/)) continue; if (msym != methodDecl.sym && scase.code) continue; JCIdent preident = null; JCExpression preexpr = null; for (JmlMethodClause clause : scase.clauses) { switch (clause.token) { case OLD: for (JCVariableDecl decl : ((JmlMethodClauseDecl)clause).decls) { JCVariableDecl newdecl = treeutils.makeVarDef(decl.type, decl.name, decl.sym.owner, convertExpr(decl.init)); newdecl.pos = decl.pos; addStat(decl); JCIdent id = treeutils.makeIdent(clause.pos, newdecl.sym); exprBiMap.put(id, convertExpr(id)); addTraceableComment(decl,clause.toString()); } break; case REQUIRES: JCExpression ex = ((JmlMethodClauseExpr)clause).expression; if (preexpr == null) preexpr = ex; else preexpr = treeutils.makeAnd(preexpr.pos, preexpr, ex); addTraceableComment(ex,clause.toString()); break; default: } } if (preexpr == null) { preexpr = treeutils.trueLit; } else { boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { preexpr = convertJML(preexpr); } catch (NoModelMethod e) { // FIXME - what to do } catch (JmlNotImplementedException e) { notImplemented("requires clause containing ",e); // FIXME - needs source continue; } finally { checkAccessEnabled = pv; } } precount++; Name prename = names.fromString(Strings.prePrefix + precount); JCVariableDecl dx = treeutils.makeVarDef(syms.booleanType, prename, methodDecl.sym, treeutils.falseLit); dx.pos = scase.pos; preident = treeutils.makeIdent(scase.pos, dx.sym); addStat(initialStats,dx); addStat(currentStatements, treeutils.makeAssignStat(scase.pos, preident, preexpr)); preconditions.put(scase, preident); if (combinedPrecondition == null || preexpr == treeutils.trueLit) { combinedPrecondition = preident; combinedPreconditionSource = scase.sourcefile; } else { combinedPrecondition = treeutils.makeOr(scase.pos, combinedPrecondition, preident); combinedPreconditionSource = scase.sourcefile; } } } methodPos = methodDecl; // If combinedPrecondition is null then there were no specs, so the implicit precondition is true and does not // need to be checked if (combinedPrecondition != null) { // FIXME - associated location? where? JavaFileObject prev = log.useSource(combinedPreconditionSource); addAssume(combinedPrecondition,Label.PRECONDITION,combinedPrecondition); log.useSource(prev); } if (rac) { Name n; JCVariableDecl vd; JCIdent ex; if (!isConstructor) { n = names.fromString("preEx"); vd = treeutils.makeVarDef(syms.runtimeExceptionType, n, methodDecl.sym, methodDecl.pos); ex = treeutils.makeIdent(methodDecl.pos,vd.sym); JCExpression str = treeutils.makeStringLiteral(methodDecl.pos,"Runtime exception while evaluating preconditions - preconditions are undefined in JML"); JCStatement st = methodCallUtilsStatement(methodDecl,"reportException",str,ex); JCBlock bl = M.at(methodDecl.pos).Block(0, List.<JCStatement>of(st)); JCCatch catcher = M.at(methodDecl.pos).Catch(vd,bl); bl = M.at(methodDecl.pos).Block(0, preStats.toList()); st = M.at(methodDecl.pos).Try(bl,List.<JCCatch>of(catcher),null); preStats = new ListBuffer<JCStatement>(); preStats.add(st); } } initialStats.appendList(preStats); paramActuals = null; clearInvariants(); // This empty block with a null label marks the end of the pre-state if (esc) { addStat(comment(methodDecl,"End of pre-state",null)); JCBlock bl = M.Block(0L, List.<JCStatement>nil()); JCStatement st = M.Labelled(null, bl); addStat(initialStats,st); } heapCount = savedheapcount; currentStatements = savedCurrentStatements; } protected void addNullnessAndTypeConditionsForInheritedFields(TypeSymbol sym, boolean beingConstructed) { for (ClassSymbol csym: utils.parents(sym, false)) { // FIXME - ??? false or true addNullnessAndTypeConditionsForFields(csym,beingConstructed); } } protected void addNullnessAndTypeConditionsForFields(TypeSymbol csym, boolean beingConstructed) { // For Java fields x: for (Symbol sy: csym.members().getElements()) { if (!(sy instanceof VarSymbol)) continue; // Find the field declaration for the given symbol Env<AttrContext> e = Enter.instance(context).getEnv(csym); if (e != null && e.tree instanceof JmlClassDecl) { JmlClassDecl cdecl = (JmlClassDecl)e.tree; for (JCTree def : cdecl.defs) { if (def instanceof JmlVariableDecl) { if (((JmlVariableDecl) def).name.toString().equals("cause")) Utils.stop(); if (((JmlVariableDecl)def).sym == sy) { addNullnessAllocationTypeCondition2((JmlVariableDecl)def, sy, beingConstructed && !utils.isJMLStatic(sy)); continue x; } } } } // FIXME - should have a DiagnosticPosition for the actual declaration addNullnessAllocationTypeCondition(methodDecl, sy, beingConstructed && !utils.isJMLStatic(sy)); } // // For JML fields // for (JCTree dd: classDecl.typeSpecs.clauses) { // if (!(dd instanceof JmlTypeClauseDecl)) continue; // JCTree t = ((JmlTypeClauseDecl)dd).decl; // if (!(t instanceof JCVariableDecl)) continue; // JCVariableDecl d = (JCVariableDecl)t; // if (d.sym == null) continue; // FIXME - model fields, at least, can have null symbols, I think // if (isConstructor && !utils.isJMLStatic(d.sym)) continue; // addNullnessAllocationTypeCondition(d,isConstructor && !utils.isJMLStatic(d.sym)); // } } protected void addPostConditions(ListBuffer<JCStatement> finalizeStats) { assumingPostConditions = false; JCMethodDecl methodDecl = this.methodDecl; boolean isConstructor = methodDecl.sym.isConstructor(); ListBuffer<JCStatement> savedCurrentStatements = currentStatements; // FIXME - need to figure out what to do for Enums. But one issue is this // Constructors are constructors for individual enum values, not for the class. // The enum values are not yet initialized while stillin the constructor (because the final assignment is not yet performed). // So postconditions are a different sort of thing. if (isConstructor && classDecl.sym.isEnum()) return; // Collect all classes that are mentioned in the method ClassCollector collector = ClassCollector.collect(this.classDecl,this.methodDecl); // if (esc) { // FIXME - what about inherited fields? fields of parameters? fields of anything else? // addStat(comment(methodDecl,"Assume field type, allocation, and nullness",null)); // for (ClassSymbol csym: utils.parents(classDecl.sym)) { // // For Java fields // for (Symbol sy: csym.members().getElements()) { // if (!(sy instanceof VarSymbol)) continue; // // FIXME - should have a DiagnosticPosition for the actual declaration // addNullnessAllocationTypeCondition(methodDecl,sy, isConstructor && !utils.isJMLStatic(sy)); // } //// // For JML fields //// for (JCTree dd: classDecl.typeSpecs.clauses) { //// if (!(dd instanceof JmlTypeClauseDecl)) continue; //// JCTree t = ((JmlTypeClauseDecl)dd).decl; //// if (!(t instanceof JCVariableDecl)) continue; //// JCVariableDecl d = (JCVariableDecl)t; //// if (d.sym == null) continue; // FIXME - model fields, at least, can have null symbols, I think //// if (isConstructor && !utils.isJMLStatic(d.sym)) continue; //// addNullnessAllocationTypeCondition(d,isConstructor && !utils.isJMLStatic(d.sym)); //// } // } // // For the parameters of the method // addStat(comment(methodDecl,"Assume parameter type, allocation, and nullness",null)); // boolean varargs = (methodDecl.sym.flags() & Flags.VARARGS) != 0; // boolean nn = true; // for (JCVariableDecl d: methodDecl.params) { // nn = addNullnessAllocationTypeCondition(d,false); // } // if (varargs && !nn) { // JCVariableDecl d = methodDecl.params.last(); // JCIdent id = treeutils.makeIdent(d.pos,d.sym); // JCExpression nnex = treeutils.makeNeqObject(d.pos,id,treeutils.nullLit); // addAssume(d.pos(),Label.IMPLICIT_ASSUME,nnex); // } // // int pos = methodDecl.pos; // // // FIXME - for the isConstructor case, it should be newly allocated // if (isConstructor) { // } else if (!utils.isJMLStatic(methodDecl.sym)){ // // Assume that 'this' is allocated // JCFieldAccess fa = treeutils.makeSelect(pos, convertCopy(currentThisExpr), isAllocSym); // addAssume(methodDecl,Label.IMPLICIT_ASSUME,fa); // // fa = treeutils.makeSelect(pos, convertCopy(currentThisExpr), allocSym); // JCExpression comp = treeutils.makeBinary(pos,JCTree.Tag.EQ,treeutils.inteqSymbol,fa,treeutils.makeIntLiteral(pos, 0)); // addAssume(methodDecl,Label.IMPLICIT_ASSUME,comp); // } // // } // /* Declare new variables, initialized to the values of the formal // * parameters, so that they can be referred to in postconditions. // */ // if (!methodDecl.params.isEmpty()) addStat(comment(methodDecl,"Declare pre-state value of formals",null)); // for (JCVariableDecl d: methodDecl.params) { // JCVariableDecl dd = treeutils.makeVarDef(d.type,M.Name(Strings.formalPrefix+d.name.toString()), // d.sym.owner, M.Ident(d.sym)); // dd.pos = dd.sym.pos = d.sym.pos; // preparams.put(d.sym,dd); // if (esc) dd.sym.owner = null; // FIXME - can get these straight from the labelmaps - do we need this? // addStat(dd); // } // Construct a condition, to be used later, that the method has not thrown an exception DiagnosticPosition methodPos = methodDecl; JCExpression noException = treeutils.makeEqObject(methodPos.getPreferredPosition(), treeutils.makeIdent(methodPos.getPreferredPosition(), exceptionSym), treeutils.nullLit); // FIXME - what if the owning class is a TypeSymbol because it is a TypeVar TypeSymbol owner = (TypeSymbol)methodDecl.sym.owner; JCExpression receiver = utils.isJMLStatic(methodDecl.sym) ? null : currentThisExpr; JCExpression savedThis = currentThisExpr; // FIXME Don't replicate the invariants in ensures and exsuresStats currentStatements = null; // Just to make sure everything is assigned to either ensuresStats or exsuresStats ListBuffer<JCStatement> ensuresStats = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> exsuresStats = new ListBuffer<JCStatement>(); // Accumulate the invariants to be checked after the method returns clearInvariants(); if (rac) { addInvariants(methodDecl,owner.type,receiver,ensuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,Label.INVARIANT_EXIT, utils.qualifiedMethodSig(methodDecl.sym)); addConstraintInitiallyChecks(methodDecl,owner,receiver,ensuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,null, utils.qualifiedMethodSig(methodDecl.sym)); } else { addInvariants(methodDecl,owner.type,receiver,ensuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,Label.INVARIANT_EXIT); addConstraintInitiallyChecks(methodDecl,owner,receiver,ensuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,null); } for (JCVariableDecl v: methodDecl.params) { if (v.type.isPrimitive()) continue; JCVariableDecl d = preparams.get(v.sym); if (isHelper(methodDecl.sym) && d.sym.type.tsym == methodDecl.sym.owner.type.tsym) continue; JCIdent id = treeutils.makeIdent(v.pos,d.sym); addInvariants(v,v.type,id,ensuresStats,true,false,false,false,true,false,Label.INVARIANT_EXIT, utils.qualifiedMethodSig(methodDecl.sym) + " (parameter " + v.name + ")"); } clearInvariants(); if (resultSym != null && !resultSym.type.isPrimitive() && !methodDecl.sym.isConstructor()) { JCIdent resultId = treeutils.makeIdent(methodDecl.pos, resultSym); addInvariants(methodDecl,resultSym.type,resultId,ensuresStats,false,false,false,false,true,false,Label.INVARIANT_EXIT, utils.qualifiedMethodSig(methodDecl.sym) + " (for result type)"); } if (rac) { addInvariants(methodDecl,owner.type,receiver,exsuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,Label.INVARIANT_EXCEPTION_EXIT, utils.qualifiedMethodSig(methodDecl.sym)); addConstraintInitiallyChecks(methodDecl,owner,receiver,exsuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,null, utils.qualifiedMethodSig(methodDecl.sym)); } else { addInvariants(methodDecl,owner.type,receiver,exsuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,Label.INVARIANT_EXCEPTION_EXIT); addConstraintInitiallyChecks(methodDecl,owner,receiver,exsuresStats,true,methodDecl.sym.isConstructor(),false,isHelper(methodDecl.sym),true,false,null); } for (JCVariableDecl v: methodDecl.params) { if (v.type.isPrimitive()) continue; JCVariableDecl d = preparams.get(v.sym); if (isHelper(methodDecl.sym) && d.sym.type.tsym == methodDecl.sym.owner.type.tsym) continue; JCIdent id = treeutils.makeIdent(v.pos,d.sym); addInvariants(v,v.type,id,exsuresStats,false,false,false,false,true,false,Label.INVARIANT_EXCEPTION_EXIT, utils.qualifiedMethodSig(methodDecl.sym) + " (parameter " + v.name + ")"); addConstraintInitiallyChecks(v,v.type.tsym,id,exsuresStats,false,false,false,false,true,false,null, utils.qualifiedMethodSig(methodDecl.sym) + " (parameter " + v.name + ")"); } // Invariants for fields savedThis = currentThisExpr; currentStatements = ensuresStats; // FIXME - also exsuresStatements? for (JCTree dd: classDecl.defs) { if (!(dd instanceof JCVariableDecl)) continue; JCVariableDecl d = (JCVariableDecl)dd; if (d.sym.type.isPrimitive()) continue; if (!utils.isJMLStatic(d.sym) && utils.isJMLStatic(methodDecl.sym)) continue; if (specs.isNonNull((JmlVariableDecl)d)) { JCExpression id = treeutils.makeIdent(d.pos, d.sym); addAssert(d, Label.NULL_FIELD, convertJML(treeutils.makeNotNull(d.pos, id))); } if (isHelper(methodDecl.sym) && d.sym.type.tsym == methodDecl.sym.owner.type.tsym) continue; boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { JCExpression fa = convertJML(treeutils.makeIdent(d.pos, d.sym)); addStat(comment(dd,"Adding exit invariants for " + d.sym,null)); addRecInvariants(false,d,fa); } finally { checkAccessEnabled = pv; } } currentThisExpr = savedThis; for (JmlTypeClauseDecl dd: specs.get(classDecl.sym).decls) { JCTree tt = dd.decl; if (!(tt instanceof JCVariableDecl)) continue; JCVariableDecl d = (JCVariableDecl)tt; if (d.sym.type.isPrimitive()) continue; if (!utils.isJMLStatic(d.sym) && utils.isJMLStatic(methodDecl.sym)) continue; JCExpression fa = convertJML(treeutils.makeIdent(d.pos, d.sym)); addStat(comment(dd,"Adding exit invariants for " + d.sym,null)); addRecInvariants(false,d,fa); } currentThisExpr = savedThis; JCBlock ensuresAxiomBlock = M.Block(0L,List.<JCStatement>nil()); ensuresStats.add(ensuresAxiomBlock); JCBlock exsuresAxiomBlock = M.Block(0L,List.<JCStatement>nil()); exsuresStats.add(exsuresAxiomBlock); // Iterate over all methods that methodDecl overrides, collecting specs boolean sawSomeSpecs = false; for (MethodSymbol msym: utils.parents(methodDecl.sym)) { if (msym.params == null) continue; // FIXME - we should do something better? or does this mean binary with no specs? JmlMethodSpecs denestedSpecs = JmlSpecs.instance(context).getDenestedSpecs(msym); ensuresStats.add(comment(methodDecl,"Asserting postconditions for " + utils.qualifiedMethodSig(msym),null)); exsuresStats.add(comment(methodDecl,"Asserting exceptional postconditions for " + utils.qualifiedMethodSig(msym),null)); // Set up the map from parameter symbol of the overridden method to // corresponding parameter of the target method. // We need this even if names have not changed, because the parameters // will have been attributed with different symbols. if (denestedSpecs.decl != null) { Iterator<JCVariableDecl> iter = denestedSpecs.decl.params.iterator(); paramActuals = new HashMap<Object,JCExpression>(); for (JCVariableDecl dp: methodDecl.params) { JCVariableDecl newdecl = iter.next(); paramActuals.put(newdecl.sym,treeutils.makeIdent(dp.pos, dp.sym)); } } else { // FIXME - why should denestedSpecs ever not have a declaration if there are any specs to use Iterator<VarSymbol> iter = msym.params.iterator(); paramActuals = new HashMap<Object,JCExpression>(); for (JCVariableDecl dp: methodDecl.params) { VarSymbol newsym = iter.next(); paramActuals.put(newsym,treeutils.makeIdent(dp.pos, dp.sym)); } } for (JmlSpecificationCase scase : denestedSpecs.cases) { sawSomeSpecs = true; if (!utils.visible(classDecl.sym, msym.owner, scase.modifiers.flags/*, methodDecl.mods.flags*/)) continue; if (msym != methodDecl.sym && scase.code) continue; JCIdent preident = preconditions.get(scase); if (preident == null) continue; // This happens if the precondition contains unimplemented material. But might in other situations as well. boolean sawSignalsOnly = false; for (JmlMethodClause clause : scase.clauses) { try { switch (clause.token) { // FIXME - would be nice if RAC postconditions could refer to the last return that was executed case ENSURES: { currentStatements = ensuresStats; axiomBlock = ensuresAxiomBlock; pushBlock(); try { JCExpression ex = ((JmlMethodClauseExpr)clause).expression; ex = convertJML(ex,preident,true); //ex = treeutils.makeImplies(clause.pos, preident, ex); addTraceableComment(ex,clause.toString()); // FIXME - if the clause is synthetic, the source file may be null, and for signals clause addAssert(true,methodDecl,Label.POSTCONDITION,ex,clause,clause.sourcefile,null); } finally { JCBlock bl = popBlock(0,clause); JCStatement st = M.at(clause.pos).If(preident, bl, null); addStat(st); } break; } case SIGNALS: // FIXME - need to check exception type of the exception { currentStatements = exsuresStats; axiomBlock = exsuresAxiomBlock; JCIdent exceptionId = treeutils.makeIdent(clause.pos,exceptionSym); { JCIdent nid = convertCopy(exceptionId); exprBiMap.put(nid, exceptionId); addTraceableComment(clause,nid,"Terminated with exception"); } JCVariableDecl vdo = ((JmlMethodClauseSignals)clause).vardef; JCVariableDecl vd = vdo; JCExpression prevValue = null; if (vd != null) { vd = treeutils.makeVarDef(vd.type,vd.name,vd.sym.owner,vd.pos); JCIdent id = treeutils.makeIdent(vdo.pos, vd.sym); prevValue = paramActuals.put(vdo.sym, id); } JCExpression ex = ((JmlMethodClauseSignals)clause).expression; addTraceableComment(ex,clause.toString()); JCExpression test = vd == null ? null : treeutils.makeInstanceOf(clause.pos,exceptionId,vd.type); pushBlock(); try { if (vd != null) { JCTypeCast tc = M.at(clause).TypeCast(vd.type, exceptionId); vd.init = esc ? exceptionId : tc; addStat(vd); } ex = convertJML(ex,preident,true); ex = treeutils.makeImplies(clause.pos, preident, ex); addAssert(methodDecl,Label.SIGNALS,ex,clause,clause.sourcefile); } finally { JCBlock block = popBlock(0,clause); if (test != null) addStat(M.at(clause.pos).If(test, block, null)); else addStat(block); if (vdo != null) paramActuals.put(vdo.sym, prevValue); } break; } case SIGNALS_ONLY: { sawSignalsOnly = true; { currentStatements = exsuresStats; axiomBlock = exsuresAxiomBlock; pushBlock(); try { JCIdent exceptionId = treeutils.makeIdent(clause.pos,exceptionSym); JCExpression condd = treeutils.falseLit; for (JCExpression t: ((JmlMethodClauseSignalsOnly)clause).list) { JCExpression tc = M.at(t).TypeTest(exceptionId, t).setType(syms.booleanType); condd = treeutils.makeOr(clause.pos, condd, tc); } condd = treeutils.makeImplies(clause.pos, preident, condd); if (rac) { addAssert(methodDecl,Label.SIGNALS_ONLY,condd,clause,clause.sourcefile, treeutils.makeUtilsMethodCall(clause.pos,"getClassName",exceptionId)); } else { addAssert(methodDecl,Label.SIGNALS_ONLY,condd,clause,clause.sourcefile); } } finally { addStat(popBlock(0,methodDecl)); } } break; } case DIVERGES: { // FIXME _ implement currentStatements = ensuresStats; axiomBlock = ensuresAxiomBlock; pushBlock(); try { JCExpression ex = ((JmlMethodClauseExpr)clause).expression; addTraceableComment(ex,clause.toString()); ex = convertJML(ex,preident,true); ex = treeutils.makeImplies(clause.pos, preident, ex); //addAssert(methodDecl,Label.SIGNALS,ex,currentStatements,clause,clause.sourcefile); } finally { popBlock(0,clause); } notImplemented(clause,clause.token.internedName() + " clause", clause.source()); break; } case DURATION: case WORKING_SPACE: { // FIXME - implement currentStatements = ensuresStats; axiomBlock = ensuresAxiomBlock; pushBlock(); try { JCExpression ex = ((JmlMethodClauseConditional)clause).expression; ex = convertJML(ex,preident,true); ex = treeutils.makeImplies(clause.pos, preident, ex); //addAssert(methodDecl,Label.SIGNALS,ex,currentStatements,clause,clause.sourcefile); } finally { popBlock(0,clause); } notImplemented(clause,clause.token.internedName() + " clause", clause.source()); break; } // FIXME - more clauses to implement? case REQUIRES: break; default: } } catch (NoModelMethod e) { JavaFileObject orig = log.useSource(clause.source()); warning(clause,"jml.skipping.no.model",""); // FIXME - can we tell which field is not implemented? log.useSource(orig); // continue - ignore any clause containing a model field that is not represented } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); continue; } } if (!sawSignalsOnly) { sawSomeSpecs = true; currentStatements = exsuresStats; axiomBlock = exsuresAxiomBlock; pushBlock(); try { JCIdent exceptionId = treeutils.makeIdent(scase.pos,exceptionSym); DiagnosticPosition pos = null; JCExpression condd = treeutils.makeThrownPredicate(scase, exceptionId, methodDecl); addAssert(methodDecl,Label.SIGNALS_ONLY,condd,pos == null ? methodDecl : pos, log.currentSourceFile(), treeutils.makeUtilsMethodCall(methodDecl.pos,"getClassName",exceptionId)); } finally { addStat(popBlock(0,methodDecl)); } } } if (!sawSomeSpecs) { currentStatements = exsuresStats; axiomBlock = exsuresAxiomBlock; pushBlock(); try { JCIdent exceptionId = treeutils.makeIdent(methodDecl.pos,exceptionSym); JCExpression rex = treeutils.makeType(methodDecl.pos,syms.runtimeExceptionType); JCExpression condd = M.at(methodDecl).TypeTest(exceptionId, rex).setType(syms.booleanType); DiagnosticPosition pos = null; for (JCExpression ex: methodDecl.thrown) { if (pos == null) pos = ex; JCExpression tc = M.at(ex).TypeTest(exceptionId, ex).setType(syms.booleanType); condd = treeutils.makeOr(ex.pos, condd, tc); } if (rac) { addAssert(methodDecl,Label.SIGNALS_ONLY,condd,pos == null ? methodDecl : pos, log.currentSourceFile(), treeutils.makeUtilsMethodCall(methodDecl.pos,"getClassName",exceptionId)); } else { addAssert(methodDecl,Label.SIGNALS_ONLY,condd,pos == null ? methodDecl : pos, log.currentSourceFile()); } } finally { addStat(popBlock(0,methodDecl)); } } } methodPos = methodDecl; if (rac) { Name n; JCVariableDecl vd; JCIdent ex; { n = names.fromString("postEx"); vd = treeutils.makeVarDef(syms.runtimeExceptionType, n, methodDecl.sym, methodDecl.pos); ex = treeutils.makeIdent(methodDecl.pos,vd.sym); JCExpression str = treeutils.makeStringLiteral(methodDecl.pos,"Runtime exception while evaluating postconditions - postconditions are undefined in JML"); JCStatement st = methodCallUtilsStatement(methodDecl,"reportException",str,ex); JCBlock bl = M.at(methodDecl.pos).Block(0, List.<JCStatement>of(st)); JCCatch catcher = M.at(methodDecl.pos).Catch(vd,bl); bl = M.at(methodDecl.pos).Block(0, ensuresStats.toList()); ensuresStats = new ListBuffer<JCStatement>(); boolean quiet = utils.jmlverbose == 0; DiagnosticPosition pos = methodDecl; JCCatch catcher1; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchMethodError").type, names.fromString("noSuchMethodError"), methodDecl.sym, pos.getPreferredPosition()); if (quiet) { catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); location = treeutils.makeNullLiteral(pos.getPreferredPosition()); // FIXME - really need to propagate the location of the call JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchMethod",id,location); catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } JCCatch catcher2; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchFieldError").type, names.fromString("noSuchFieldError"), methodDecl.sym, pos.getPreferredPosition()); if (quiet) { catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); if (Utils.testingMode) location = treeutils.makeNullLiteral(pos.getPreferredPosition()); JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchField",id, location); catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } if (!bl.stats.isEmpty()) { if (isOnlyComment(bl)) { ensuresStats.add(bl); } else { st = M.at(methodDecl.pos).Try(bl,List.<JCCatch>of(catcher,catcher1,catcher2),null); ensuresStats.add(st); } } } if (!isConstructor) { n = names.fromString("sigEx"); vd = treeutils.makeVarDef(syms.runtimeExceptionType, n, methodDecl.sym, methodDecl.pos); ex = treeutils.makeIdent(methodDecl.pos,vd.sym); JCExpression str = treeutils.makeStringLiteral(methodDecl.pos,"Runtime exception while evaluating exceptional postconditions - signals clauses are undefined in JML"); JCStatement st = methodCallUtilsStatement(methodDecl,"reportException",str,ex); JCBlock bl = M.at(methodDecl.pos).Block(0, List.<JCStatement>of(st)); JCCatch catcher = M.at(methodDecl.pos).Catch(vd,bl); bl = M.at(methodDecl.pos).Block(0, exsuresStats.toList()); exsuresStats = new ListBuffer<JCStatement>(); boolean quiet = utils.jmlverbose == 0; DiagnosticPosition pos = methodDecl; JCCatch catcher1; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchMethodError").type, names.fromString("noSuchMethodError"), methodDecl.sym, pos.getPreferredPosition()); if (quiet) { catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); location = treeutils.makeNullLiteral(pos.getPreferredPosition()); // FIXME - really need to propagate the location of the call JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchMethod",id,location); catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } JCCatch catcher2; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchFieldError").type, names.fromString("noSuchFieldError"), methodDecl.sym, pos.getPreferredPosition()); if (quiet) { catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); if (Utils.testingMode) location = treeutils.makeNullLiteral(pos.getPreferredPosition()); JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchField",id, location); catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } if (!bl.stats.isEmpty()) { if (isOnlyComment(bl)) { exsuresStats.add(bl); } else { st = M.at(methodDecl.pos).Try(bl,List.<JCCatch>of(catcher,catcher1,catcher2),null); exsuresStats.add(st); } } } } if (ensuresStats.isEmpty() && exsuresStats.isEmpty()) { // skip } else { JCStatement ifstat = M.at(methodPos).If(noException,M.Block(0, ensuresStats.toList()),M.Block(0,exsuresStats.toList())); finalizeStats.add(ifstat); } paramActuals = null; clearInvariants(); currentStatements = savedCurrentStatements; axiomBlock = null; assumingPostConditions = true; } protected void addInstanceInitialization() { JmlSpecs.TypeSpecs tspecs = specs.get(classDecl.sym); // If there is an initializer, use it if (tspecs != null) { for (JmlTypeClause tc: tspecs.clauses) { if (tc instanceof JmlTypeClauseInitializer && (tc.modifiers.flags & Flags.STATIC) == 0) { JmlTypeClauseInitializer tci = (JmlTypeClauseInitializer)tc; addStat( comment(methodDecl,"Instance initializer",null)); for (JmlSpecificationCase cs: tci.specs.cases) { JCExpression pre = null; JCExpression post = null; for (JmlMethodClause clause: cs.clauses) { if (clause.token == JmlTokenKind.REQUIRES) { JCExpression p = convertJML(((JmlMethodClauseExpr)clause).expression); pre = pre == null ? p : treeutils.makeAnd(clause.pos, pre, p); } else if (clause.token == JmlTokenKind.ENSURES) { JCExpression p = convertJML(((JmlMethodClauseExpr)clause).expression); post = post == null ? p : treeutils.makeAnd(clause.pos, post, p); } else { notImplemented(clause,"Clause not implemented in an initializer: " + clause.token.internedName()); } } if (post == null) post = treeutils.trueLit; addAssume(tc,Label.POSTCONDITION,post); } return; // There must be at most one initializer } } } addStat( comment(methodDecl,"Class fields for constructor",null)); // First pass sets everything to zero-equivalent java.util.List<JCVariableDecl> redo = new LinkedList<JCVariableDecl>(); for (JCTree t: classDecl.defs) { if (!(t instanceof JCVariableDecl)) continue; JCVariableDecl vd = (JCVariableDecl)t; if (utils.isJMLStatic(vd.sym)) continue; // FIXME - static fields have to be created sooner if (isModel(vd.sym)) continue; JCExpression receiver; receiver = treeutils.makeIdent(vd.pos, vd.sym); receiver = convertJML(receiver); JCExpression z = treeutils.makeZeroEquivalentLit(vd.pos,vd.type); addStat(treeutils.makeAssignStat(vd.pos, receiver, z)); if (vd.init != null) redo.add(vd); } // Second pass computes the initialized result for (JCVariableDecl vd: redo) { JCExpression receiver; receiver = treeutils.makeIdent(vd.pos, vd.sym); receiver = convertJML(receiver); JCExpression e = convertExpr(vd.init); e = addImplicitConversion(e, vd.type, e); addStat(treeutils.makeAssignStat(vd.pos, receiver, e)); } } protected void addStaticInitialization(ClassSymbol csym) { JmlSpecs.TypeSpecs tspecs = specs.get(csym); // If there is a static initializer, use it if (tspecs != null) { for (JmlTypeClause tc: tspecs.clauses) { if (tc instanceof JmlTypeClauseInitializer && (tc.modifiers.flags & Flags.STATIC) != 0) { JmlTypeClauseInitializer tci = (JmlTypeClauseInitializer)tc; for (JmlSpecificationCase cs: tci.specs.cases) { JCExpression pre = null; JCExpression post = null; for (JmlMethodClause clause: cs.clauses) { if (clause.token == JmlTokenKind.REQUIRES) { JCExpression p = convertJML(((JmlMethodClauseExpr)clause).expression); pre = pre == null ? p : treeutils.makeAnd(clause.pos, pre, p); } else if (clause.token == JmlTokenKind.ENSURES) { JCExpression p = convertJML(((JmlMethodClauseExpr)clause).expression); post = post == null ? p : treeutils.makeAnd(clause.pos, post, p); } else { notImplemented(clause,"Clause not implemented in an initializer: " + clause.token.internedName()); } } if (post == null) post = treeutils.trueLit; addAssume(tc,Label.POSTCONDITION,post); } return; // There must be at most one static initializer } } } // // Otherwise we construct it from the static fields // // Env<AttrContext> enva = Enter.instance(context).getEnv(csym); // if (enva == null) return; // JmlClassDecl decl = (JmlClassDecl)enva.tree; // if (decl == null) return; // // for (JCTree t: decl.defs) { // if (t instanceof JmlVariableDecl) { // JmlVariableDecl d = (JmlVariableDecl)t; // if (!d.sym.isStatic()) continue; // JCIdent id = treeutils.makeIdent(d.pos, d.sym); // JCExpression z = treeutils.makeZeroEquivalentLit(d.pos,d.type); // addStat(treeutils.makeAssignStat(d.pos, id, z)); // } // } // // for (JCTree t: decl.defs) { // if (t instanceof JmlVariableDecl) { // JmlVariableDecl d = (JmlVariableDecl)t; // if (!d.sym.isStatic()) continue; // if (d.init == null) continue; // JCIdent id = treeutils.makeIdent(d.pos, d.sym); // JCExpression z = addImplicitConversion(d, d.type, convertExpr(d.init)); // addStat(treeutils.makeAssignStat(d.pos, id, z)); // } // } } // VISITOR METHODS // OK @Override public void visitTopLevel(JCCompilationUnit that) { // OpenJML should never call this, because JCCompilationUnit nodes should be // replaced by JmlCompilationUnit nodes. We implement this just in case, but // always produce a JmlCompilationUnit node if (pureCopy) { JmlCompilationUnit n = M.at(that).TopLevel(that.packageAnnotations,that.pid,convert(that.defs)); n.docComments = that.docComments; // TODO: This needs to be remapped n.endPositions = that.endPositions; // TODO: This needs to be remapped // n.flags = that.flags; //n.mode = that.mode; n.lineMap = that.lineMap; n.namedImportScope = that.namedImportScope; n.packge = that.packge; n.setType(that.type); n.sourcefile = that.sourcefile; n.starImportScope = that.starImportScope; //n.specsCompilationUnit = that.specsCompilationUnit; //n.specsTopLevelModelTypes = that.specsTopLevelModelTypes; result = n; } if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitTopLevel while translating JML: " + that.getClass()); return; } error(that,"Unexpected call of JmlAssertionAdder.visitTopLevel: " + that.getClass()); } // OK @Override public void visitImport(JCImport that) { // OpenJML should never call this, because JCImport nodes should be // replaced by JmlImport nodes. We implement this just in case, but // always produce a JmlImport node JCTree qualid = that.qualid; if (fullTranslation) qualid = convert(qualid); result = M.at(that).Import(qualid, that.staticImport); } // OK @Override public void visitClassDef(JCClassDecl that) { // OpenJML should never call this, because JCClassDecl nodes should be // replaced by JmlClassDecl nodes. We implement this just in case, but // always produce a JmlClassDecl node. if (pureCopy) { JmlClassDecl d = M.at(that).ClassDef( convert(that.mods), that.name, convert(that.typarams), convert(that.extending), convert(that.implementing), convert(that.defs)); d.setType(that.type); d.docComment = null; d.env = null; d.specsDecl = null; d.superSymbol = null; d.sym = that.sym; d.thisSymbol = null; d.toplevel = null; d.typeSpecs = null; result = d; classBiMap.put((JmlClassDecl)that,d); } else if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitClassDef while translating JML: " + that.getClass()); } else { error(that, "Unexpectedly calling JmlAssertionAdder.visitClassDef: " + that.getClass()); } return; } // OK @Override public void visitMethodDef(JCMethodDecl that) { // In OpenJML, we expect to always have JmlMethodDecl nodes, and so // never to call this visit class // However, just in case a user creates one, we translate it if (pureCopy) { JmlMethodDecl m = M.at(that).MethodDef( convert(that.mods), that.name, convert(that.restype), convert(that.typarams), convert(that.recvparam), convert(that.params), convert(that.thrown), convert(that.body), convert(that.defaultValue)); m.setType(that.type); m.sym = that.sym; m.cases = null; m._this = null; m.docComment = null; m.methodSpecsCombined = null; //m.owner = that.owner; m.sourcefile = null; m.specsDecl = null; result = m; methodBiMap.put((JmlMethodDecl)that,m); } else if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitMethodDef while translating JML: " + that.getClass()); } else { error(that,"Unexpected visit call in JmlAssertionAdder.visitMethodDef: " + that.getClass()); } return; } // OK @Override public void visitVarDef(JCVariableDecl that) { if (pureCopy) { JmlVariableDecl v = M.at(that).VarDef( convert(that.mods), that.name, convert(that.vartype), convert(that.init)); v.setType(that.type); v.sym = that.sym; // Keep same sym, or else we have to map them v.docComment = null; v.fieldSpecs = null; v.fieldSpecsCombined = null; v.sourcefile = null; v.specsDecl = null; result = v; } else if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitVarDef while translating JML: " + that.getClass()); } else { error(that,"Unexpected visit call in JmlAssertionAdder.visitVarDef: " + that.getClass()); } return; } //OK @Override public void visitSkip(JCSkip that) { if (!pureCopy) addTraceableComment(that); result = addStat(M.at(that).Skip()); // Caution - JML statements are subclasses of JCSkip } /** Produces a MethodSym for an initialization block; any variables * declared within an initialization block need a 'method' as an owner * rather than a class, or they will be interpreted as class fields. */ protected JmlMethodDecl methodSymForInitBlock(DiagnosticPosition pos, long flags, JCClassDecl classDecl) { MethodSymbol msym = new MethodSymbol( flags, classDecl.name, null, classDecl.sym); methodDecl = M.MethodDef( M.Modifiers(flags, M.Annotations(List.<com.sun.tools.javac.code.Attribute.Compound>nil())), classDecl.name, null, null, null, null, null, null, //body, null); methodDecl.pos = pos.getPreferredPosition(); methodDecl.sourcefile = Log.instance(context).currentSourceFile(); methodDecl.docComment = null; methodDecl.cases = null; methodDecl.methodSpecsCombined = null; methodDecl.sym = msym; methodDecl.type = null; return methodDecl; } //OK @Override public void visitBlock(JCBlock that) { JmlMethodDecl prev = this.methodDecl; boolean isInInitializerBlock = currentStatements == null; if (isInInitializerBlock) { // We are in an initializer block // We need a method symbol to be the owner of declarations // (otherwise they will have the class as owner and be thought to // be fields) methodDecl = methodSymForInitBlock(that, that.flags, classDecl); } pushBlock(); boolean pv = checkAccessEnabled; // FIXME - when to check access of initializer stastements? checkAccessEnabled = false; try { if (isInInitializerBlock) { initialize2(that.flags & Flags.STATIC); } scan(that.stats); } finally { checkAccessEnabled = pv; JCBlock bl = popBlock(that.flags,that); if (isInInitializerBlock) { // FIXME - need a better way to tell which kind of block we are in classDefs.add(bl); } else { addStat(bl); } methodDecl = prev; result = bl; } } // OK - should call visitJmlDoWhileLoop instead @Override public void visitDoLoop(JCDoWhileLoop that) { error(that,"Unexpected visit call in JmlAssertionAdder.visitDoLoop: " + that.getClass()); } // OK - should call visitJmlWhileLoop instead @Override public void visitWhileLoop(JCWhileLoop that) { error(that,"Unexpected visit call in JmlAssertionAdder.visitWhileLoop: " + that.getClass()); } // OK - should call visitJmlForLoop instead @Override public void visitForLoop(JCForLoop that) { error(that,"Unexpected visit call in JmlAssertionAdder.visitForLoop: " + that.getClass()); } // OK - should call visitJmlEnhancedForLoop instead @Override public void visitForeachLoop(JCEnhancedForLoop that) { error(that,"Unexpected visit call in JmlAssertionAdder.visitForeachLoop: " + that.getClass()); } /** This map contains an entry for all labels that are in scope. It is used * when an \old expression appears that references the label: in RAC mode we * need to insert a computation of the desired quantity. */ // FIXME - determine if these need to be two separate lists. The difference at present is that those in the active list are not yet closed. protected Map<Name,ListBuffer<JCStatement>> labelActiveOldLists = new HashMap<Name,ListBuffer<JCStatement>>(); protected Map<Name,ListBuffer<JCStatement>> labelOldLists = new HashMap<Name,ListBuffer<JCStatement>>(); //OK @Override public void visitLabelled(JCLabeledStatement that) { if (!pureCopy) addStat(comment(that,"label:: " + that.label + ": ...",null)); // Note that the labeled statement will turn into a block // Since declarations may not be labelled statements, there are no // declarations in the labelled statement that need to be in scope after // the labelled statement. JCLabeledStatement stat = M.at(that).Labelled(that.label, null); markLocation(that.label,currentStatements,stat); treeMap.put(that, stat); // we store the mapping from old to new so that we can appropriately translate the targets of break and continue statements labelActiveOldLists.put(that.label, currentStatements); JCBlock block; try { block = convertIntoBlock(that,that.body); stat.body = block; result = addStat(stat); } finally { labelActiveOldLists.remove(that.label); labelOldLists.put(that.label, currentStatements); treeMap.remove(that); } } //OK @Override public void visitSwitch(JCSwitch that) { JCExpression switchExpr = that.selector; if (!pureCopy) { addStat(traceableComment(that,that,"switch " + that.getExpression() + " ...","Selection")); } try { JCExpression selector = convertExpr(switchExpr); if (!pureCopy) { if (that.selector.type.equals(syms.stringType)) { if (javaChecks) { JCExpression e = treeutils.makeNeqObject(switchExpr.pos, selector, treeutils.nullLit); addAssert(that.selector, Label.POSSIBLY_NULL_VALUE, e); } } else if ((that.selector.type.tsym.flags_field & Flags.ENUM) != 0) { if (javaChecks) { JCExpression e = treeutils.makeNeqObject(switchExpr.pos, selector, treeutils.nullLit); addAssert(switchExpr, Label.POSSIBLY_NULL_VALUE, e); } } else { selector = addImplicitConversion(switchExpr,syms.intType,selector); } } JCSwitch sw = M.at(that).Switch(selector, null); // record the translation from old to new AST before translating the body treeMap.put(that,sw); ListBuffer<JCCase> cases = new ListBuffer<JCCase>(); for (JCCase c: that.cases) { JCExpression pat = convertExpr(c.pat); JCBlock b = convertIntoBlock(c,c.stats); b.stats = b.stats.prepend(traceableComment(c,c,(c.pat == null ? "default:" : "case " + c.pat + ":"),null)); JCCase cc = M.at(c.pos).Case(pat,b.stats); cases.add(cc); } sw.cases = cases.toList(); result = addStat(sw.setType(that.type)); } finally { treeMap.remove(that); } } //OK @Override public void visitCase(JCCase that) { // JCCase is handled directly in visitSwitch error(that,"JmlAssertionAdder.visitCase should not be called"); } // OK except concurrency checks @Override public void visitSynchronized(JCSynchronized that) { if (!pureCopy) { addTraceableComment(that,"synchronized " + that.getExpression() + " ..."); } JCExpression lock = convertExpr(that.lock); if (that.lock instanceof JCParens && ((JCParens)that.lock).expr instanceof JCIdent && ((JCIdent)((JCParens)that.lock).expr).name.toString().equals("this")) { // Don't need to check that 'this' is not null, though this is a complicated and error-prone check } else if (javaChecks) { JCExpression e = treeutils.makeNeqObject(that.lock.pos, lock, treeutils.nullLit); addAssert(that.lock, Label.POSSIBLY_NULL_VALUE, e); } JCBlock block = convertBlock(that.body); result = addStat(M.at(that).Synchronized(lock, block).setType(that.type)); // FIXME - need to add concurrency checks } // OK // FIXME - review and cleanup for both esc and rac @Override public void visitTry(JCTry that) { if (!pureCopy) addStat(comment(that,"try ...",null)); // Don't need to trace the try keyword JCBlock body = convertBlock(that.body); List<JCCatch> catchers = null; if (that.catchers != null) { ListBuffer<JCCatch> ncatchers = new ListBuffer<JCCatch>(); for (JCCatch catcher: that.catchers) { pushBlock(); JCIdent id = treeutils.makeIdent(catcher.param, catcher.param.sym); addRecInvariants(true,catcher.param,id); JCBlock block = popBlock(0, catcher.param); block.stats = block.stats.prepend(comment(catcher.getParameter(),"catch (" + catcher.param +") ...",null)); JCBlock bl = convertBlock(catcher.getBlock()); //block.stats = block.stats.prepend(traceableComment(catcher.getParameter(),catcher.getParameter(),"catch (" + catcher.param +") ...")); block.stats = block.stats.append(bl); // EXCEPTION = NULL int sp = catcher.getParameter().getStartPosition(); JCIdent exId = treeutils.makeIdent(sp, exceptionSym); treeutils.copyEndPosition(exId, catcher.getParameter()); JCStatement st = treeutils.makeAssignStat(sp, exId, treeutils.nullLit); block.stats = block.stats.prepend(st); JCIdent nid = treeutils.makeIdent(sp, catcher.getParameter().sym); treeutils.copyEndPosition(nid, catcher.getParameter()); exprBiMap.put(nid,convertCopy(nid)); // block.stats = block.stats.prepend(traceableComment(catcher.getParameter(),nid,"Exception caught")); // FIXME - the nid is not being evaluated for the trace // TERMINATION = 0 JCIdent termid = treeutils.makeIdent(catcher.pos,terminationSym); block.stats = block.stats.prepend(treeutils.makeAssignStat(catcher.pos, termid, treeutils.zero)); JCVariableDecl odecl = catcher.getParameter(); JmlVariableDecl decl = M.at(odecl).VarDef(odecl.sym, null); // Catcher declarations have no initializer JCCatch ncatcher = M.at(catcher).Catch(decl,block); ncatcher.setType(catcher.type); ncatchers.add(ncatcher); } catchers = ncatchers.toList(); } JCBlock finalizer = convertBlock(that.finalizer); List<JCTree> newResources = convertCopy(that.resources); // FIXME - no checks implemented on the resources JCTry st = M.at(that).Try(newResources, body, catchers, finalizer); st.setType(that.type); result = addStat(st); return; } // OK @Override public void visitCatch(JCCatch that) { // Catch statements are handled along with Try error(that,"JmlAssertionAdder.visitCatch should not be called"); } // FIXME - these are not fully developed yet protected void adjustWellDefinedConditions(JCExpression cond) { adjustWellDefinedConditions(cond,wellDefinedConditions); } protected void adjustWellDefinedConditions(JCExpression cond, java.util.List<JmlStatementExpr> conds) { for (JmlStatementExpr st: conds) { st.expression = treeutils.makeImplies(st.expression.pos, cond, st.expression); } } // OK @Override public void visitConditional(JCConditional that) { if (!pureCopy) addStat(comment(that," ... conditional ...",null)); JCExpression cond = convertExpr(that.cond); if (pureCopy) { JCExpression truepart = convertExpr(that.truepart); JCExpression falsepart = convertExpr(that.falsepart); result = eresult = M.at(that).Conditional(cond,truepart,falsepart).setType(that.type); } else if (cond instanceof JCLiteral) { Boolean v = (Boolean)((JCLiteral)cond).getValue(); if (v) { result = eresult = convertExpr(that.truepart); } else { result = eresult = convertExpr(that.falsepart); } } else if (!splitExpressions) { JCExpression prev = condition; try { java.util.List<JmlStatementExpr> listf = new java.util.LinkedList<JmlStatementExpr>(); listf.addAll(wellDefinedConditions); cond = addImplicitConversion(cond,syms.booleanType,cond); condition = treeutils.makeAnd(that.pos, prev, cond); JCExpression truepart = convertExpr(that.truepart); truepart = addImplicitConversion(that.truepart,that.type,truepart); adjustWellDefinedConditions(cond); condition = treeutils.makeAnd(that.pos, prev, treeutils.makeNot(that.falsepart.pos, cond)); JCExpression falsepart = convertExpr(that.falsepart); falsepart = addImplicitConversion(that.falsepart,that.type,falsepart); adjustWellDefinedConditions(cond,listf); wellDefinedConditions.addAll(listf); result = eresult = M.at(that).Conditional(cond,truepart,falsepart).setType(that.type); } finally { condition = prev; } } else { cond = addImplicitConversion(cond,syms.booleanType,cond); Name resultname = names.fromString(Strings.conditionalResult + (++count)); JCVariableDecl vdecl = treeutils.makeVarDef(that.type, resultname, esc? null : methodDecl.sym, that.pos); addStat(vdecl); ListBuffer<JCStatement> checkA = pushBlock(); if (that.truepart.toString().startsWith("(string.indexOf(',') >= 0 ||")) Utils.stop(); JCBlock trueblock = null; try { JCExpression tres = convertExpr(that.truepart); tres = addImplicitConversion(that.truepart,that.type,tres); JCIdent id = treeutils.makeIdent(that.truepart.pos, vdecl.sym); addStat( treeutils.makeAssignStat(that.truepart.pos, id, tres)); } finally { trueblock = popBlock(0,that.truepart,checkA); } checkA = pushBlock(); JCBlock falseblock = null; try { JCExpression fres = convertExpr(that.falsepart); fres = addImplicitConversion(that.falsepart,that.type,fres); JCIdent id = treeutils.makeIdent(that.falsepart.pos, vdecl.sym); addStat( treeutils.makeAssignStat(that.falsepart.pos, id, fres)); } finally { falseblock = popBlock(0,that.falsepart,checkA); } JCStatement stat = M.at(that).If(cond, trueblock, falseblock); addStat(stat); result = eresult = treeutils.makeIdent(that.pos, vdecl.sym); } } // OK @Override public void visitIf(JCIf that) { if (pureCopy) { JCExpression cond = convertExpr(that.cond); JCStatement thenpart = convert(that.thenpart); JCStatement elsepart = convert(that.elsepart); JCStatement st = M.at(that).If(cond,thenpart,elsepart).setType(that.type); result = addStat( st ); } else { addStat(traceableComment(that,that,"if " + that.getCondition() + " ...", "Condition")); JCExpression cond = convertExpr(that.cond); cond = addImplicitConversion(that.cond,syms.booleanType,cond); // The scanned result of the then and else parts must always be a block // because multiple statements might be produced, even from a single // statement in the branch. JCBlock thenpart = convertIntoBlock(that.thenpart,that.thenpart); JCBlock elsepart = that.elsepart == null ? null : convertIntoBlock(that.elsepart, that.elsepart); JCStatement st = M.at(that).If(cond,thenpart,elsepart).setType(that.type); result = addStat( st ); } } // FIXME - document these protected void addTraceableComment(JCTree t) { JCStatement c = comment(t); pathMap.put(t, c); addStat(c); } protected void addTraceableComment(JCTree t, String description) { addStat(traceableComment(t,t,description,null)); } protected void addTraceableComment(JCTree t, JCExpression expr, String description) { addStat(traceableComment(t,expr,description,null)); } protected void addTraceableComment(JCTree t, JCTree expr, String description, String info) { addStat(traceableComment(t,expr,description,info)); } /** Create a comment that is used for tracing; the value of the given expression (and subexpressions) * is printed as part of the trace. * @param t the source location to cite for the comment * @param expr the expression to trace * @param description the description of the expression to give (typically a string representation of the symbolic expression) * @param info additional information to be provided with the comment * @return */ protected JCStatement traceableComment(JCTree t, JCTree expr, String description, String info) { JavaFileObject source = null; if (t instanceof JmlTree.JmlSource) source = ((JmlTree.JmlSource)t).source(); JmlStatementExpr c = comment(t,description,source); c.id = info; pathMap.put(expr, c); return c; } @Override public void visitExec(JCExpressionStatement that) { if (!pureCopy) { addTraceableComment(that); } JCExpression arg = convertExpr(that.getExpression()); // For rac and esc, assignments become a new variable, // so no exec statement is needed, and arg is then an ident. // Similarly, method invocations that return a value become a // temporary id, and no additional Exec statement is needed // Otherwise we create an Exec statement if (arg instanceof JCMethodInvocation || arg instanceof JCAssign || arg instanceof JCAssignOp || arg instanceof JCUnary) { result = addStat( M.at(that).Exec(arg).setType(that.type) ); } } // OK @Override public void visitBreak(JCBreak that) { if (!pureCopy) { addTraceableComment(that); } JCBreak st = M.at(that).Break(that.label); st.target = treeMap.get(that.target); if (st.target == null) { error(that,"Unknown break target"); } st.setType(that.type); result = addStat(st); } // OK @Override public void visitContinue(JCContinue that) { if (!pureCopy) { addTraceableComment(that); } if (that.label == null && !pureCopy) { // The translation of loops puts the body of a loop in its own // block so that continue statements can break out of it and go // to do the 'step' part of the loop. The continue should be // exiting the innermost loop, but a break may have a more inner // block - so we always put a label and set the target. JCBreak st = M.at(that).Break(null); st.setType(that.type); st.label = continueStack.peek().getLabel(); st.target = continueStack.peek(); result = addStat(st); } else { JCContinue st = M.at(that).Continue(that.label); st.setType(that.type); st.target = treeMap.get(that.target); if (st.target == null) { error(that,"Unknown continue target"); } result = addStat(st); } } // OK @Override public void visitReturn(JCReturn that) { if (!pureCopy) addTraceableComment(that); JCExpression retValue = convertExpr(that.getExpression()); if (!pureCopy) { int p = that.pos; if (retValue != null) { // Checks for empty returns retValue = addImplicitConversion(that,methodDecl.restype.type,retValue); JCIdent resultid = treeutils.makeIdent(p,resultSym); JCStatement stat = treeutils.makeAssignStat(p,resultid,retValue); addStat(stat); retValue = treeutils.makeIdent(p,resultSym); } // Record the value of the termination location JCIdent id = treeutils.makeIdent(p,terminationSym); JCLiteral intlit = treeutils.makeIntLiteral(p,that.pos); JCStatement stat = treeutils.makeAssignStat(p,id,intlit); addStat(stat); // If the return statement is in a finally block, there may have been an exception // in the process of being thrown - so we set EXCEPTION to null. id = treeutils.makeIdent(p,exceptionSym); stat = treeutils.makeAssignStat(p,id,treeutils.nullLit); addStat(stat); } result = addStat( M.at(that).Return(retValue).setType(that.type) ); } // OK // FIXME - review for esc @Override public void visitThrow(JCThrow that) { if (pureCopy) { JCExpression expr = convertExpr(that.getExpression()); result = addStat(M.at(that).Throw(expr).setType(that.type)); // } else if (rac) { // addTraceableComment(that); // // JCExpression expr = convertExpr(that.getExpression()); // JCIdent id = treeutils.makeIdent(that.pos,exceptionSym); // addStat( treeutils.makeAssignStat(that.pos,id,expr) ); // JCExpression e = treeutils.makeNeqObject(that.expr.pos, expr, treeutils.makeNullLiteral(that.expr.getEndPosition(log.currentSource().getEndPosTable()))); // if (javaChecks) addAssert(e, Label.POSSIBLY_NULL_VALUE, e); // result = addStat(M.at(that).Throw(expr).setType(that.type)); } else { addTraceableComment(that); pushBlock(); try { JCExpression exceptionExpr = convertExpr(that.expr); // assert expr != null; JCExpression e = treeutils.makeNeqObject(that.expr.pos, exceptionExpr, treeutils.makeNullLiteral(that.expr.getEndPosition(log.currentSource().getEndPosTable()))); if (javaChecks) addAssert(e, Label.POSSIBLY_NULL_VALUE, e); if (that.expr.type.getTag() != TypeTag.BOT) { // Declare a local variable of the type of the exception expression, initialized to the expression Name local = names.fromString(Strings.exceptionLocalVarString + that.pos); JCVariableDecl decl = treeutils.makeVarDef(that.expr.type,local,exceptionSym.owner,exceptionExpr); addStat(decl); // Set the value of the global EXCEPTION variable to the expression JCIdent id = treeutils.makeIdent(that.pos,exceptionSym); JCIdent localid = treeutils.makeIdent(exceptionExpr.pos,decl.sym); JCExpressionStatement assign = treeutils.makeAssignStat(that.pos,id,localid); exprBiMap.put(that, assign); exprBiMap.put(that, assign.expr); addStat(assign); // Assign the termination value JCIdent tid = treeutils.makeIdent(that.pos,terminationSym); JCStatement term = treeutils.makeAssignStat(that.pos, tid, treeutils.makeIntLiteral(that.pos,-that.pos)); addStat(term); // throw the local expression value localid = treeutils.makeIdent(that.pos,decl.sym); exceptionExpr = localid; } JCThrow thrw = M.at(that).Throw(exceptionExpr); addStat(thrw); } finally { JCBlock block = popBlock(0,that); result = addStat(block); } } return; } // OK - this is a Java assert statement @Override public void visitAssert(JCAssert that) { // FIXME - in esc we may want to treat this as an exception throwing Java statement // ESC will eventually convert this to a Jml assertion, but RAC wants // it left as a Java assertion statement if (!pureCopy) addTraceableComment(that); JCExpression cond = convertExpr(that.getCondition()); JCExpression opt = that.getDetail(); JCExpression info = // We don't convert literals to avoid bloat when the literal is an explicit null or constant String !(opt instanceof JCLiteral) ? convertExpr(opt) : fullTranslation ? treeutils.makeDuplicateLiteral(opt.pos, (JCLiteral)opt) : opt; if (pureCopy) { result = addStat( M.at(that).Assert(cond, info).setType(that.type) ); } else if (rac) { if (JmlOption.isOption(context,JmlOption.RAC_JAVA_CHECKS)) { result = addAssert(true,that,Label.EXPLICIT_ASSERT,cond,null,null,info); if (info != null) newTemp(info); // The detail expression is evaluated but not used anywhere } result = addStat( M.at(that).Assert(cond, info).setType(that.type) ); } else { // esc result = addAssert(true,that,Label.EXPLICIT_ASSERT,cond,null,null,info); if (info != null) newTemp(info); // The detail expression is evaluated but not used anywhere } } // FIXME - review all the assignable checks for appropriate use of translations and this /** Returns true if x is contained in the datagroup y */ protected boolean isContainedIn(Symbol x, Symbol y) { if (x == y) return true; if (x == classDecl.thisSymbol && y == currentThisId.sym) return true; FieldSpecs fs = specs.getSpecs((VarSymbol)x); if (fs == null) { // null can happen for a private field of a class without source // and without a declaration in the corresponding jml file return false; } for (JmlTypeClause tc: fs.list) { if (tc.token == JmlTokenKind.IN) { JmlTypeClauseIn inclause = (JmlTypeClauseIn)tc; for (JmlGroupName gn: inclause.list) { if (isContainedIn(gn.sym,y)) return true; } } } return false; } /** Check that the given storeref is a subset of the given * pstoreref, returning the condition under which the storeref * is allowed. */ protected JCExpression accessAllowed(JmlStoreRefKeyword storeref, JCExpression pstoreref) { DiagnosticPosition pos = storeref; JmlTokenKind token = storeref.token; if (token == JmlTokenKind.BSNOTSPECIFIED) token = JmlTokenKind.BSEVERYTHING; if (token == JmlTokenKind.BSNOTHING) return treeutils.trueLit; if (pstoreref instanceof JmlStoreRefKeyword) { JmlTokenKind ptoken = ((JmlStoreRefKeyword)pstoreref).token; if (token == JmlTokenKind.BSEVERYTHING && ptoken == JmlTokenKind.BSEVERYTHING) return treeutils.trueLit; if (token == JmlTokenKind.BSEVERYTHING && ptoken == JmlTokenKind.BSNOTHING) return treeutils.falseLit; } else if (pstoreref instanceof JCIdent) { if (token == JmlTokenKind.BSEVERYTHING) return treeutils.falseLit; } else if (pstoreref instanceof JCFieldAccess) { if (token == JmlTokenKind.BSEVERYTHING) return treeutils.falseLit; } else if (pstoreref instanceof JCArrayAccess) { if (token == JmlTokenKind.BSEVERYTHING) return treeutils.falseLit; } else if (pstoreref instanceof JmlStoreRefArrayRange) { if (token == JmlTokenKind.BSEVERYTHING) return treeutils.falseLit; } log.error(pos,"esc.not.implemented", "Assignability comparison: " + storeref + " vs. " + pstoreref); return treeutils.falseLit; } /** Check that the given id is a subset of the given * pstoreref, returning the condition under which the id * is allowed. */ protected JCExpression accessAllowed(JCIdent id, JCExpression pstoreref, JCExpression baseThisExpr, JCExpression targetThisExpr) { if (id.sym.owner == null || id.sym.owner.kind == Kinds.MTH) return treeutils.trueLit; // Local variable // FIXME - I thought this was already checked somewhere DiagnosticPosition pos = id; if (pstoreref instanceof JmlStoreRefKeyword) { JmlTokenKind ptoken = ((JmlStoreRefKeyword)pstoreref).token; if (ptoken == JmlTokenKind.BSNOTSPECIFIED) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSEVERYTHING) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSNOTHING) return treeutils.falseLit; } else if (id.name == names._this) { return treeutils.trueLit; } else if (pstoreref instanceof JCIdent) { JCIdent pid = (JCIdent)pstoreref; // Presumes any class fields are qualified return id.sym == pid.sym ? treeutils.trueLit : treeutils.falseLit; // if (id.sym == pid.sym) { // if (baseThisSym == targetThisSym) return treeutils.trueLit; // JCExpression id1 = treeutils.makeIdent(id.pos, targetThisSym); // JCExpression id2 = treeutils.makeIdent(pstoreref.pos, baseThisSym); // return treeutils.makeEqObject(id.pos, id1, id2); // } // else return treeutils.falseLit; } else if (pstoreref instanceof JCFieldAccess) { JCFieldAccess pfa = (JCFieldAccess)pstoreref; if (pfa.name == null) { log.error(pstoreref, "jml.internal", "A field wildcard expression should not be present here"); return treeutils.falseLit; } // FIXME - check the following - use isContainedIn JCExpression sel = pfa.selected; Symbol s0 = sel instanceof JCIdent ? ((JCIdent)sel).sym : sel instanceof JCFieldAccess ? ((JCFieldAccess)sel).sym : null; while (true) { if (sel instanceof JCArrayAccess) { sel = ((JCArrayAccess)sel).indexed; continue; } if (sel instanceof JmlStoreRefArrayRange) { sel = ((JmlStoreRefArrayRange)sel).expression; continue; } break; } Symbol s = sel instanceof JCIdent ? ((JCIdent)sel).sym : sel instanceof JCFieldAccess ? ((JCFieldAccess)sel).sym : null; boolean st = utils.isJMLStatic(id.sym); if (id.sym != pfa.sym && pfa.sym != null) return treeutils.falseLit; if (st && pfa.sym != null) return treeutils.trueLit; if (st && pfa.sym == null && s0 == classDecl.sym) return treeutils.trueLit; if (st && pfa.sym == null && s0 != classDecl.sym) return treeutils.falseLit; if (!st && pfa.sym == null) { if (s instanceof Symbol.ClassSymbol) return treeutils.falseLit; } JCExpression result = treeutils.makeEqObject(pos.getPreferredPosition(), targetThisExpr, convertJML(pfa.selected)); // FIXME - needs check for not implemented return result; } else if (pstoreref instanceof JCArrayAccess) { return treeutils.falseLit; } else if (pstoreref instanceof JmlStoreRefArrayRange) { return treeutils.falseLit; } log.error(pos,"esc.not.implemented", "Assignability comparison: " + id + " vs. " + pstoreref); return treeutils.falseLit; } /** Check that the given storeref is a subset of the given * pstoreref, returning the condition under which the storeref * is allowed. */ protected JCExpression accessAllowed(JCFieldAccess fa, JCExpression pstoreref, JCExpression baseThisExpr, JCExpression targetThisExpr) { if (fa.name == null) { log.error(pstoreref, "jml.internal", "A field wildcard expression should not be present here"); return treeutils.falseLit; } if (fa.name == names.length) { // The .length pseudo-field of an array is always accessible if (fa.selected.type.hasTag(TypeTag.ARRAY)) return treeutils.trueLit; } DiagnosticPosition pos = fa; int posp = pos.getPreferredPosition(); JCExpression pfac = convertAssignable(pstoreref,baseThisExpr,true); if (pfac instanceof JmlStoreRefKeyword) { JmlTokenKind ptoken = ((JmlStoreRefKeyword)pfac).token; if (ptoken == JmlTokenKind.BSNOTSPECIFIED) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSEVERYTHING) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSNOTHING) return treeutils.falseLit; } else if (pfac instanceof JCIdent) { JCIdent pid = (JCIdent)pfac; if (!isContainedIn(fa.sym,pid.sym)) return treeutils.falseLit; if (utils.isJMLStatic(pid.sym)) return treeutils.trueLit; JCExpression idthis = treeutils.makeOld(pos, baseThisExpr); if (rac) idthis = convertJML(idthis); JCExpression result = treeutils.makeEqObject(posp, idthis, convertJML(fa.selected)); return result; } else if (pfac instanceof JCFieldAccess) { JCFieldAccess pfa = (JCFieldAccess)pfac; if (pfa.name == null) { JCExpression or = treeutils.falseLit; for (Symbol s: utils.listJmlVisibleFields(pfa.selected.type.tsym, methodDecl.mods.flags&Flags.AccessFlags, treeutils.isATypeTree(pfa.selected))) { JCExpression newpfa = M.at(pfa.pos).Select(pfa.selected, s); or = treeutils.makeOr(pfa.pos, or, accessAllowed(fa,newpfa,baseThisExpr,targetThisExpr)); // if (s != fa.sym) continue; // if (fa.sym.isStatic()) { // // If it is the same symbol and is static, then it is definitely the same storeref // or = treeutils.trueLit; // } else { // or = treeutils.makeOrSimp(pfa.pos, or, treeutils.makeEqObject(pfa.pos, convertJML(fa.selected), pfa.selected)); // } } return or; //log.error(pstoreref, "jml.internal", "A field wildcard expression should not be present here"); //return treeutils.falseLit; } boolean contained = isContainedIn((VarSymbol)fa.sym,(VarSymbol)pfa.sym); if (!contained) { // a.x vs b.y with x != y, so automatically false return treeutils.falseLit; } if (contained && !utils.isJMLStatic(pfa.sym)) { // a.x vs. b.x with x not static, so result is (a == b) JCExpression result = treeutils.makeEqObject(posp, convertJML(fa.selected), convertJML(pfa.selected)); return result; } if (contained && utils.isJMLStatic(pfa.sym)) { // a.x vs. b.x with x static, so result is true - a and b have to be the same type return treeutils.trueLit; } // if (pfa.sym == null) { // FIXME - review this // // a.x vs b.* (x may be *, a,b may be expressions or types) // // Note: this.* does not include static fields, and only include jmlvisible fields and data groups // // JCExpression expr = fa.selected; // Symbol fs = expr instanceof JCIdent ? ((JCIdent)expr).sym : // expr instanceof JCFieldAccess ? ((JCFieldAccess)expr).sym : // null; // expr = convertJML(pfa.selected); // Symbol pfs = expr instanceof JCIdent ? ((JCIdent)expr).sym : // expr instanceof JCFieldAccess ? ((JCFieldAccess)expr).sym : // null; // // if (fa.sym != null && !utils.jmlvisible( // FIXME - what should the answer be from a jmlvisibility if we are matching x.* and y.* ? // pfs instanceof TypeSymbol ? pfs : pfs.enclClass(), // fa.sym.owner, // fa.sym.flags(), methodDecl.mods.flags)) { // return treeutils.falseLit; // } // // if (fa.sym != null && (pfs instanceof Symbol.TypeSymbol) != utils.isJMLStatic(fa.sym)) return treeutils.falseLit; // if (fa.sym == null && (pfs instanceof Symbol.TypeSymbol) != (fs instanceof Symbol.TypeSymbol)) return treeutils.falseLit; // // // FIXME - this all needs review // if (pfs instanceof Symbol.ClassSymbol) { // // ?.x vs X.* // JCExpression result = (fs instanceof Symbol.ClassSymbol) && (fs == pfs) // FIXME - should be fs extends pfs? // ? treeutils.trueLit : treeutils.falseLit; // return result; // } else if (fs instanceof Symbol.ClassSymbol) { // // X.x vs e.* // boolean same = fs == pfs; // JCExpression result = same ? treeutils.trueLit : treeutils.falseLit; // return result; // } else { // // ee.x vs. e.* // JCExpression result = treeutils.makeEqObject(posp, convertJML(fa.selected), expr); // return result; // } // } } else if (pfac instanceof JCArrayAccess) { return treeutils.falseLit; } else if (pfac instanceof JmlStoreRefArrayRange) { return treeutils.falseLit; } log.error(pos,"esc.not.implemented", "Assignability comparison: " + fa + " vs. " + pfac); return treeutils.falseLit; } /** Check that the given storeref is a subset of the given * pstoreref, returning the condition under which the storeref * is allowed. */ protected JCExpression accessAllowed(JCArrayAccess aa, JCExpression pstoreref, JCExpression baseThisExpr, JCExpression targetThisExpr) { DiagnosticPosition pos = aa; int posp = pos.getPreferredPosition(); if (pstoreref instanceof JmlStoreRefKeyword) { JmlTokenKind ptoken = ((JmlStoreRefKeyword)pstoreref).token; if (ptoken == JmlTokenKind.BSNOTHING) return treeutils.falseLit; if (ptoken == JmlTokenKind.BSNOTSPECIFIED) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSEVERYTHING) return treeutils.trueLit; } else if (pstoreref instanceof JCIdent) { return treeutils.falseLit; } else if (pstoreref instanceof JCFieldAccess) { return treeutils.falseLit; } else if (pstoreref instanceof JCArrayAccess) { JCArrayAccess paa = (JCArrayAccess)pstoreref; JCExpression a1 = convertAssignable(aa.indexed, targetThisExpr, true); JCExpression e = treeutils.makeOld(pos,paa.indexed); if (rac) e = convertAssignable(e, baseThisExpr, true); JCExpression result = treeutils.makeEqObject(posp, a1, e); if (paa.index == null ) return result; if (aa.index == null) return treeutils.falseLit; a1 = convertJML(aa.index); result = treeutils.makeAnd(posp,result, treeutils.makeBinary(posp,JCTree.Tag.EQ,treeutils.inteqSymbol,a1,convertJML(paa.index))); return result; } else if (pstoreref instanceof JmlStoreRefArrayRange) { JmlStoreRefArrayRange paa = (JmlStoreRefArrayRange)pstoreref; JCExpression condition = treeutils.trueLit; while (true) { JCExpression addedcondition; if (aa.index == null) { if (paa.lo == null && paa.hi == null) addedcondition = treeutils.trueLit; else if (paa.hi == null) addedcondition = treeutils.makeBinary(pos.getPreferredPosition(), JCTree.Tag.EQ, treeutils.inteqSymbol, convertJML(paa.lo),treeutils.zero); // FIXME - compare paa.hi to array.length, paa.lo to zero if not null else return treeutils.falseLit; } else { JCExpression aat = convert(aa.index); JCExpression loCondition = treeutils.makeBinary(posp,JCTree.Tag.LE,treeutils.intleSymbol, paa.lo == null ? treeutils.zero : convertJML(paa.lo), aat); addedcondition = treeutils.makeAnd(posp, loCondition, paa.hi == null ? treeutils.trueLit : treeutils.makeBinary(posp, JCTree.Tag.LE,treeutils.intleSymbol, aat, convertJML(paa.hi)) ); } condition = condition == treeutils.trueLit ? addedcondition : treeutils.makeAnd(posp,addedcondition,condition); if (!(paa.expression instanceof JmlStoreRefArrayRange)) break; if (!(aa.indexed instanceof JCArrayAccess)) return treeutils.falseLit; paa = (JmlStoreRefArrayRange)paa.expression; aa = (JCArrayAccess)aa.indexed; } JCExpression a1 = convertJML(aa.indexed); JCExpression arrayConverted = treeutils.makeOld(pos,convertJML(paa.expression)); if (rac) arrayConverted = convertJML(arrayConverted); // FIXME - why is this done twice? JCExpression result = treeutils.makeAnd(posp, treeutils.makeEqObject(posp, a1, arrayConverted), condition); return result; } log.error(pos,"esc.not.implemented", "Assignability comparison: " + aa + " vs. " + pstoreref); return treeutils.falseLit; } /** Check that the given storeref is a subset of the given * pstoreref, returning the condition under which the storeref * is allowed. */ protected JCExpression accessAllowed(JmlStoreRefArrayRange aa, JCExpression pstoreref, JCExpression baseThisExpr, JCExpression targetThisExpr) { DiagnosticPosition pos = aa; int posp = pos.getPreferredPosition(); if (pstoreref instanceof JmlStoreRefKeyword) { JmlTokenKind ptoken = ((JmlStoreRefKeyword)pstoreref).token; if (ptoken == JmlTokenKind.BSEVERYTHING) return treeutils.trueLit; if (ptoken == JmlTokenKind.BSNOTHING) return treeutils.falseLit; if (ptoken == JmlTokenKind.BSNOTSPECIFIED) return treeutils.trueLit; } else if (pstoreref instanceof JCIdent) { return treeutils.falseLit; } else if (pstoreref instanceof JCFieldAccess) { return treeutils.falseLit; } else if (pstoreref instanceof JCArrayAccess) { JCArrayAccess paa = (JCArrayAccess)pstoreref; JCExpression e = treeutils.makeOld(pos,(paa.indexed)); e = convertAssignable(e,baseThisExpr,true); JCExpression result = treeutils.makeEqObject(posp, convertAssignable(aa.expression,targetThisExpr,true), e); if (paa.index == null) return result; JCExpression paat = convertJML(paa.index); result = treeutils.makeAnd(posp, result, treeutils.makeBinary(posp,JCTree.Tag.EQ,treeutils.inteqSymbol, aa.lo == null ? treeutils.zero : convertJML(aa.lo), paat)); // result = treeutils.makeAnd(pos, result, treeutils.makeBinary(pos,JCTree.Tag.EQ,treeutils.inteqSymbol, // aa.hi == null ? /* FIXME - array length -1 */ : jmlrewriter.translate(aa.hi), paat)); return result; } else if (pstoreref instanceof JmlStoreRefArrayRange) { JmlStoreRefArrayRange paa = (JmlStoreRefArrayRange)pstoreref; JCExpression e = treeutils.makeOld(pos,(paa.expression)); e = convertAssignable(e,baseThisExpr,true); JCExpression result = treeutils.makeEqObject(posp, convertAssignable(aa.expression,targetThisExpr,true), e); JCExpression a1 = aa.lo == null ? treeutils.zero : convertJML(aa.lo); JCExpression a2 = paa.lo == null ? treeutils.zero : convertJML(paa.lo); result = treeutils.makeAnd(posp, result, treeutils.makeBinary(posp,JCTree.Tag.LE,treeutils.intleSymbol, a2, a1)); JCIdent savedId = currentThisId; JCExpression savedExpr = currentThisExpr; try { currentThisExpr = targetThisExpr; currentThisId = (JCIdent)currentThisExpr; a1 = aa.hi != null ? convertJML(aa.hi) : treeutils.makeBinary(posp, JCTree.Tag.MINUS, treeutils.makeLength(pos, convertJML(aa.expression)), treeutils.one); currentThisExpr = baseThisExpr; currentThisId = (JCIdent)currentThisExpr; a2 = paa.hi != null ? convertJML(paa.hi) : treeutils.makeBinary(posp, JCTree.Tag.MINUS, treeutils.makeLength(pos, convertJML(paa.expression)), treeutils.one); } finally { currentThisId = savedId; currentThisExpr = savedExpr; } result = treeutils.makeAnd(posp, result, treeutils.makeBinary(pos.getPreferredPosition(),JCTree.Tag.LE,treeutils.intleSymbol, a1, a2)); return result; } log.error(pos,"esc.not.implemented", "Assignability comparison: " + aa + " vs. " + pstoreref); return treeutils.falseLit; } /** Check that the given storeref is a subset of the given * pstoreref, returning the condition under which the storeref * is allowed. storeref may not be a field wildcard. * pstoreref is presumed to refer to the enclosing methodDecl. */ protected JCExpression accessAllowed(JCExpression storeref, JCExpression pstoreref, JCExpression baseThisExpr, JCExpression targetThisExpr) { if (storeref instanceof JmlStoreRefKeyword) { return accessAllowed((JmlStoreRefKeyword)storeref,pstoreref); } else if (storeref instanceof JCIdent) { return accessAllowed((JCIdent)storeref,pstoreref,baseThisExpr,targetThisExpr); } else if (storeref instanceof JCFieldAccess) { // JCFieldAccess fa = (JCFieldAccess)storeref; // if (fa.name == null) { // JCExpression or = treeutils.falseLit; // for (Symbol s: utils.listJmlVisibleFields(fa.selected.type.tsym, methodDecl.mods.flags&Flags.AccessFlags, utils.isJMLStatic(methodDecl.sym))) { // JCExpression newfa = M.at(fa.pos).Select(fa.selected, s); // or = treeutils.makeOr(fa.pos, or, assignmentAllowed(newfa,pstoreref,baseThisSym,targetThisSym)); // } // return or; // } else { return accessAllowed((JCFieldAccess)storeref,pstoreref,baseThisExpr,targetThisExpr); // } } else if (storeref instanceof JCArrayAccess) { return accessAllowed((JCArrayAccess)storeref,pstoreref,baseThisExpr,targetThisExpr); } else if (storeref instanceof JmlStoreRefArrayRange) { return accessAllowed((JmlStoreRefArrayRange)storeref,pstoreref,baseThisExpr,targetThisExpr); } log.error(storeref.pos,"esc.not.implemented", "Assignability comparison: " + storeref + " vs. " + pstoreref); return treeutils.falseLit; } protected boolean recursiveCall = false; protected boolean checkAccessEnabled; /** Add assertions that the lhs is allowed to be written to or read from. * * @param assignPosition the position of the generation assertion * @param mdecl the method in which the assignment takes place and whose assignable clauses are to be checked * @param lhs the target to be checked if it may be assigned to (must not be a field wildcard) * @param baseThisSym * @param targetThisSym */ protected void checkAccess(JmlTokenKind token, DiagnosticPosition assignPosition, JCExpression origlhs, JCExpression lhs, JCExpression baseThisExpr, JCExpression targetThisExpr) { if (rac) return; // FIXME - turn off checking assignable until we figure out how to handle fresh allocations in rac if (recursiveCall) return; if (!checkAccessEnabled && token == JmlTokenKind.ACCESSIBLE) return; Symbol sym = treeutils.getSym(lhs); if (sym != null && !(sym instanceof VarSymbol)) return; recursiveCall = true; boolean noSpecCases = true; for (JmlSpecificationCase c: specs.getDenestedSpecs(methodDecl.sym).cases) { noSpecCases = false; JCExpression check = checkAccess(token,assignPosition, origlhs, lhs, c,baseThisExpr,targetThisExpr); // FIXME - not sure about the lhs,lhs if (!treeutils.isTrueLit(check)) { // The access is not allowed if it is nowhere in the // assignable/accessible clauses; we point to the first one. If there are // none the default is \everything, which is always allowed // (constructor default handled below) DiagnosticPosition cpos = c; for (JmlMethodClause m : c.clauses) { if (m.token == token) { cpos = m; break; } } check = makeAssertionOptional(check); addAssert(assignPosition, token == JmlTokenKind.ASSIGNABLE ? Label.ASSIGNABLE : Label.ACCESSIBLE, check,cpos,c.sourcefile,origlhs.toString()); } } if (noSpecCases) { // Constructor default is handled within the call below JCExpression check = checkAccess(token,assignPosition, lhs,lhs,M.at(methodDecl.pos).JmlSpecificationCase(null, false, null, null, List.<JmlMethodClause>nil()),currentThisId,currentThisId); if (!treeutils.isTrueLit(check)) { check = makeAssertionOptional(check); addAssert(assignPosition, token == JmlTokenKind.ASSIGNABLE ? Label.ASSIGNABLE : Label.ACCESSIBLE, check,methodDecl,methodDecl.sourcefile,origlhs.toString()); } } recursiveCall = false; } /** This method checks a store-ref of a callee against the caller's assignable clauses. * Both the callee and the caller are expected to have field wildcards expanded. * * @param callPosition the position of the call * @param scannedItem the store-ref of the callee * @param precondition the callee precondition under which the store-ref might be assigned * @param baseThisId the thisId of the caller * @param targetThisId the thisId of the callee */ protected void checkAgainstCallerSpecs(JmlTokenKind token, DiagnosticPosition callPosition, JCExpression scannedItem, JCExpression precondition, JCIdent baseThisId, /*@nullable*/ JCIdent targetThisId, JavaFileObject itemSource) { if (rac) return; // FIXME - turn off checking assignable until we figure out how to handle fresh allocations if (token == JmlTokenKind.ACCESSIBLE && !checkAccessEnabled) return; JmlMethodSpecs mspecs = specs.getDenestedSpecs(methodDecl.sym); // FIXME - does this contain all inherited specs? it should if (mspecs == null) return; // FIXME - why would this happen? JCExpression sc; { sc = convertAssignable(scannedItem,targetThisId,true); if (sc instanceof JCFieldAccess && !utils.isJMLStatic(((JCFieldAccess)sc).sym)) { JCExpression obj = ((JCFieldAccess)sc).getExpression(); JCExpression fresh = isFreshlyAllocated(scannedItem,obj); JCExpression notfresh = treeutils.makeNot(scannedItem, fresh); //if (scannedItem.type.isPrimitive() || jmltypes.isJmlType(scannedItem.type)) fresh = treeutils.falseLit; if (treeutils.isFalseLit(fresh)) { // no change to precondition } else if (!treeutils.isTrueLit(fresh)) { precondition = treeutils.makeAnd(scannedItem, precondition, notfresh); } else { return; // Definitely fresh - so no checks to be done } } } for (JmlSpecificationCase c : mspecs.cases) { JCExpression condition = checkAccess(token,callPosition, scannedItem, sc, c, baseThisId, targetThisId); if (condition != treeutils.trueLit) { condition = treeutils.makeImplies(scannedItem.pos, precondition, condition); condition = makeAssertionOptional(condition); String message = scannedItem.toString(); addAssert(callPosition,token == JmlTokenKind.ASSIGNABLE ? Label.ASSIGNABLE : Label.ACCESSIBLE,condition,c,c.sourcefile,message); // FIXME - do we also want to identify the position or identity of the scannedItem? } } } /** Check that the given storeref is allowed by the given * specification case, returning the condition under which the storeref * is allowed. * * @param assignPosition the position of the generated expression * @param storeref the target to be checked if it may be assigned to (must not be a field wildcard) * @param specCase the specification case of the containing method against which to check * @param baseThisSym * @param targetThisSym */ protected @Nullable JCExpression checkAccess(JmlTokenKind token, DiagnosticPosition assignPosition, JCExpression orig, JCExpression storeref, JmlSpecificationCase specCase, JCExpression baseThisExpr, JCExpression targetThisExpr) { // If the storeref is a local identifier, then assignment is allowed if ((storeref instanceof JCIdent) && ((JCIdent)storeref).sym.owner instanceof Symbol.MethodSymbol) return treeutils.trueLit; if (currentFresh != null && (storeref instanceof JCFieldAccess) && ((JCFieldAccess)storeref).sym == currentFresh.sym) return treeutils.trueLit; JCIdent pre = preconditions.get(specCase); pre = pre == null ? null : treeutils.makeIdent(pre.pos, pre.sym); // a new id for the same symbol boolean anyAccessClauses = false; JmlMethodClause anyAssignableClause = null; JCExpression asg = treeutils.falseLit; boolean saved = convertingAssignable; try { convertingAssignable = true; if (storeref instanceof JmlStoreRefKeyword) { asg = treeutils.falseLit; } else if (storeref instanceof JCFieldAccess && ((JCFieldAccess)storeref).name == null) { // something.* asg = treeutils.falseLit; // } else if (storeref.type.isPrimitive() || jmltypes.isJmlType(storeref.type)) { // asg = treeutils.falseLit; } else { // FIXME - ASSIGNABLEs coming in are already converted // but at least some ACCESSIBLEs are not JCExpression sc = token == JmlTokenKind.ACCESSIBLE ? convertAssignable(storeref,targetThisExpr,false) : storeref; asg = isFreshlyAllocated(assignPosition,sc); // convertAssignable(storeref,(VarSymbol)baseThisSym)); // FIXME _ base or target } } finally { convertingAssignable = saved; } for (JmlMethodClause mclause : specCase.clauses) { try { // FIXME - do we have to satisfy each assignable clause individually, or the union? if (mclause.token == token) { // Is storeref allowed by some item in the parent method's list? List<JCExpression> pstorerefs = expandStoreRefList(((JmlMethodClauseStoreRef)mclause).list, methodDecl.sym); if (token == JmlTokenKind.ACCESSIBLE) { pstorerefs = expandStoreRefListAll(((JmlMethodClauseStoreRef)mclause).list, methodDecl.sym); } for (JCExpression pstoreref: pstorerefs) { JCExpression nasg = accessAllowed(storeref,pstoreref, baseThisExpr, targetThisExpr); // optimizing asg = asg || nasg asg = nasg == treeutils.trueLit ? nasg : asg == treeutils.falseLit ? nasg : nasg == treeutils.falseLit ? asg : asg == treeutils.trueLit ? asg : treeutils.makeOr(storeref.pos,asg,nasg); } anyAccessClauses = true; } if (mclause.token == JmlTokenKind.ASSIGNABLE && anyAssignableClause != null) anyAssignableClause = mclause; } catch (JmlNotImplementedException e) { notImplemented("assignable/accessible clause containing ",e); // FIXME - clause source } } // If there are no accessible clauses at all, then we use a default accessible clause; // If there are no assignable clauses at all, then we use a default assignable clause; // The default assignable clause is \everything, except for constructors for which it is this.* // FIXME - for now, the default if no accessible clause is \everything if (!anyAccessClauses) { List<JCExpression> pstorerefs; if (token == JmlTokenKind.ACCESSIBLE && anyAssignableClause != null) { pstorerefs = ((JmlMethodClauseStoreRef)anyAssignableClause).list; if (token == JmlTokenKind.ACCESSIBLE) pstorerefs = List.<JCExpression>of(M.JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING)); } else if (methodDecl.sym.isConstructor()) { pstorerefs = defaultStoreRefs(specCase, methodDecl.sym); if (token == JmlTokenKind.ACCESSIBLE) pstorerefs = List.<JCExpression>of(M.JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING)); } else { pstorerefs = List.<JCExpression>of(M.JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING)); } try { for (JCExpression pstoreref: pstorerefs) { JCExpression nasg = accessAllowed(storeref,pstoreref,baseThisExpr,targetThisExpr); // optimizing asg = asg || nasg asg = nasg == treeutils.trueLit ? nasg : asg == treeutils.falseLit ? nasg : nasg == treeutils.falseLit ? asg : asg == treeutils.trueLit ? asg : treeutils.makeOr(storeref.pos,asg,nasg); } } catch (JmlNotImplementedException e) { notImplemented("assignable/accessible clause containing ",e); // FIXME - clause source } } if (asg != treeutils.trueLit && pre != null) { return treeutils.makeImplies(storeref.pos, pre, asg); } else { return asg; } } /** Returns an expression indicating whether the object being dereferenced in the storeref * is allocated now but was not in the prestate. */ protected JCExpression isFreshlyAllocated(DiagnosticPosition pos, JCExpression storeref) { if (rac) return treeutils.falseLit; // FIXME - how do we handle this aspect of assignment checking in RAC. // if (storeref instanceof JmlStoreRefKeyword) return treeutils.falseLit; // if (storeref.type.hasTag(TypeTag.BOT) || storeref.type.isPrimitive()) return treeutils.falseLit; JCExpression obj = null; if (storeref instanceof JCFieldAccess && !utils.isJMLStatic(((JCFieldAccess)storeref).sym)) { obj = ((JCFieldAccess)storeref).selected; } else if (storeref instanceof JCArrayAccess) { obj = ((JCArrayAccess)storeref).indexed; } else if (storeref instanceof JmlStoreRefArrayRange) { obj = ((JmlStoreRefArrayRange)storeref).expression; } else if (storeref instanceof JCIdent) { if (((JCIdent)storeref).name == names._this) return treeutils.falseLit; obj = storeref; } if (obj == null) return treeutils.falseLit; // obj = convertJML(obj); // FIXME - in some cases at least this is a second conversion if (true || !convertingAssignable) obj = newTemp(obj); if (false && defaultOldLabel != null) { // FIXME - don't bother with the following if obj is 'this' JCExpression allocNow = M.at(pos).Select(obj, isAllocSym).setType(syms.booleanType); JCExpression allocOld = treeutils.makeOld(pos.getPreferredPosition(),M.at(pos).Select(convertCopy(obj), isAllocSym).setType(syms.booleanType), M.at(pos.getPreferredPosition()).Ident(defaultOldLabel)); return treeutils.makeAnd(pos.getPreferredPosition(),allocNow,treeutils.makeNot(pos.getPreferredPosition(), allocOld)); } else { JCExpression allocCount = M.at(pos).Select(obj, allocSym).setType(syms.intType); JCExpression fresh = treeutils.makeBinary(pos, JCTree.Tag.GT, allocCount, treeutils.makeIntLiteral(pos, 0)); // FIXME - should this be using the defaultOldLabel somehow? return fresh; } } /** Returns an expression indicating whether an object is allocated in the current state. */ protected JCFieldAccess isAllocated(DiagnosticPosition pos, JCExpression obj) { //if (rac) return treeutils.falseLit; // FIXME - how do we handle this aspect of assignment checking in RAC. obj = convertJML(obj); return (JCFieldAccess)M.at(pos).Select(obj, isAllocSym).setType(syms.booleanType); } /** Expands any store-ref wildcard items, since they depend on the base location and visibility */ // FIXME - should we expand data groups? protected List<JCExpression> expandStoreRefList(List<JCExpression> list, MethodSymbol base) { ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (JCExpression item: list) { if (item instanceof JCFieldAccess && ((JCFieldAccess)item).name == null) { JCFieldAccess fa = (JCFieldAccess)item; java.util.List<VarSymbol> exlist; // if (fa.getExpression().toString().equals("this")) { // exlist = utils.listAllFields((TypeSymbol)base.owner, treeutils.isATypeTree(((JCFieldAccess)item).selected)); // } else { // FIXME - use jml visibility (spec_public and spec_protected?) exlist = utils.listJmlVisibleFields((TypeSymbol)base.owner, base.flags() & Flags.AccessFlags, treeutils.isATypeTree(((JCFieldAccess)item).selected)); // } for (VarSymbol vsym : exlist) { newlist.add(M.at(item).Select(fa.selected, vsym)); } } else { newlist.add(item); } } return newlist.toList(); } protected List<JCExpression> expandStoreRefListAll(List<JCExpression> list, MethodSymbol base) { ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (JCExpression item: list) { if (item instanceof JCFieldAccess && ((JCFieldAccess)item).name == null) { JCFieldAccess fa = (JCFieldAccess)item; // FIXME - use jml visibility (spec_public and spec_protected?) java.util.List<VarSymbol> exlist = utils.listAllFields((TypeSymbol)base.owner, treeutils.isATypeTree(((JCFieldAccess)item).selected)); for (VarSymbol vsym : exlist) { newlist.add(M.at(item).Select(fa.selected, vsym)); } } else { newlist.add(item); } } return newlist.toList(); } /** Returns the list of store-ref items corresponding to this.* */ // FIXME - should we expand data groups? protected List<JCExpression> defaultStoreRefs(DiagnosticPosition pos, MethodSymbol base) { ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (VarSymbol vsym : utils.listJmlVisibleFields((TypeSymbol)base.owner, base.flags() & Flags.AccessFlags, utils.isJMLStatic(base))) { newlist.add(M.at(pos).Select(currentThisExpr, vsym)); } return newlist.toList(); } // FIXME - needs work @Override public void visitApply(JCMethodInvocation that) { //System.out.println("APPLY ENTER " + statementStack.size()); // FIXME - needs result set - needs proper handling of pure methods etc. if (that.meth.toString().startsWith("System.out.")) { // We handle System.out.println specially. It is essentially pure and // does not depend on any class invariants, so we can safely just call it // after translating the arguments. This avoids bloat caused by putting // in debug statements. List<JCExpression> args = convertExprList(that.args); JCMethodInvocation app = M.at(that).Apply(that.typeargs, that.meth, args).setType(that.type); app.varargsElement = that.varargsElement; // a Type result = eresult = app; return; } // if (inOldEnv && rac) { // String msg = "RAC does not currently implement method calls in \\old expressions: " + treeutils.getSym(that.meth); // notImplemented(that,msg); // throw new JmlNotImplementedException(that,msg); // } if (!translatingJML && !pureCopy) checkThatMethodIsCallable(that, treeutils.getSym(that.meth)); if ((translatingJML && rac) || pureCopy) { // FIXME - need to check definedness by testing preconditions List<JCExpression> typeargs = convertExprList(that.typeargs); JCExpression meth = convertExpr(that.meth); List<JCExpression> args = convertExprList(that.args); JCMethodInvocation app = M.at(that).Apply(typeargs, meth, args).setType(that.type); app.varargsElement = that.varargsElement; // a Type result = eresult = app; return; } applyHelper(that); } // sym == null means \everything must be callable protected void checkThatMethodIsCallable(DiagnosticPosition pos, Symbol sym) { // Method might be called in an initializer??? FIXME - what do we do then? // FIXME - not sure how to detect this if (methodDecl.body == null) return; // If there are no specification cases, then the default is that // everything is callable for (JmlSpecificationCase specCase: specs.getDenestedSpecs(methodDecl.sym).cases) { JCIdent pre = preconditions.get(specCase); pre = pre == null ? null : treeutils.makeIdent(pre.pos, pre.sym); // a new id for the same symbol for (JmlMethodClause mclause : specCase.clauses) { try { // We have to satisfy each callable clause individually JCExpression asg = null; if (mclause.token == JmlTokenKind.CALLABLE) { JmlMethodClauseCallable c = (JmlMethodClauseCallable)mclause; if (c.keyword != null) { if (c.keyword.token == JmlTokenKind.BSNOTHING) { asg = treeutils.falseLit; } else if (c.keyword.token == JmlTokenKind.BSEVERYTHING) { asg = null; } else if (c.keyword.token == JmlTokenKind.BSNOTSPECIFIED) { asg = null; } } else if (sym == null) { asg = treeutils.falseLit; } else { asg = treeutils.falseLit; for (JmlMethodSig sig: c.methodSignatures) { if (sig.methodSymbol != sym) continue; asg = null; break; } } } // If there are no assignable clauses at all, // then we use a default assignable clause. if (asg == null) { // OK } else { // asg is false asg = treeutils.makeNot(mclause.pos, pre); asg = makeAssertionOptional(asg); String msg = sym == null ? JmlTokenKind.BSEVERYTHING.internedName() : utils.qualifiedMethodSig((MethodSymbol)sym); msg = msg + " is not callable"; addAssert(pos,Label.CALLABLE,asg,mclause,specCase.sourcefile,msg); } } catch (JmlNotImplementedException e) { notImplemented("callable clause containing ",e); // FIXME - clause source } } } } protected List<JCExpression> convertArgs(DiagnosticPosition pos, List<JCExpression> args, List<Type> argtypes, boolean hasVarArgs) { // Note: because the declaration may have a last varargs element, // args.size() may be greater than argtypes.size() But since everything // has typechecked OK, it is OK to implicitly use the last element of // argtypes for any additional args. ListBuffer<JCExpression> out = new ListBuffer<JCExpression>(); Iterator<Type> iter = argtypes.iterator(); boolean last = false; Type currentArgType = null; boolean usedVarArgs = args.size() == 0 && argtypes.size() != 0 && hasVarArgs; for (JCExpression a: args) { a = convertExpr(a); if (iter.hasNext()) currentArgType = iter.next(); // handles varargs last = !iter.hasNext(); if (last && hasVarArgs && !(a.type instanceof Type.ArrayType)) { currentArgType = ((Type.ArrayType)argtypes.last()).getComponentType(); a = addImplicitConversion(a,currentArgType,a); usedVarArgs = true; } else { a = addImplicitConversion(a,currentArgType,a); } if (useMethodAxioms && translatingJML) { } else if ((a instanceof JCIdent) && ((JCIdent)a).name.toString().startsWith(Strings.tmpVarString)) { } else if ((a instanceof JCIdent) && localVariables.containsKey(((JCIdent)a).sym)) { } else if (!localVariables.isEmpty()) { } else { // This check is a hack and a bit expensive. It makes sure that // every argument is represented by a temporary variable. // Without this an argument that is just an Ident or a Literal // and is not used ends up without its value captured for // tracing a = newTemp(a); } out.add(a); } if (usedVarArgs) { ListBuffer<JCExpression> newout = new ListBuffer<JCExpression>(); int n = argtypes.length() - 1; while (--n >= 0) { newout.add(out.remove()); } // create an array with the rest currentArgType = ((Type.ArrayType)argtypes.last()).getComponentType(); List<JCExpression> dims = List.<JCExpression>nil(); // FIXME - what if the array is multi-dimensional int p = pos.getPreferredPosition(); JCExpression t = treeutils.makeType(p,currentArgType); JCExpression e = M.at(p).NewArray(t, dims, out.toList()); e.type = argtypes.last(); if (esc) e = convertExpr(e); newout.add(newTemp(e)); // FIXME - see comment above about newTemp out = newout; } return out.toList(); } public boolean useMethodAxioms; public java.util.List<Symbol> completedInvariants = new LinkedList<Symbol>(); public java.util.Set<Symbol> inProcessInvariants = new HashSet<Symbol>(); protected boolean startInvariants(Symbol csym, DiagnosticPosition pos) { if (completedInvariants.contains(csym)) return true; // skip processing if (inProcessInvariants.add(csym)) return false; // ok to do processing log.error(pos, "jml.recursive.invariants", csym.getQualifiedName()); return true; // skip processing } protected boolean startInvariantsNoError(Symbol csym, DiagnosticPosition pos) { if (completedInvariants.contains(csym)) return true; // skip processing if (inProcessInvariants.add(csym)) return false; // ok to do processing return true; // skip processing } protected void endInvariants(Symbol csym) { inProcessInvariants.remove(csym); completedInvariants.add(csym); } protected void clearInvariants() { completedInvariants.clear(); inProcessInvariants.clear(); } protected void changeState() { if (esc) { ++heapCount; int p = methodDecl.pos; // FIXME - better position? JCStatement assign = treeutils.makeAssignStat(p, treeutils.makeIdent(p,heapSym), treeutils.makeIntLiteral(p, heapCount)); // treeutils.makeBinary(p, JCTree.Tag.PLUS, treeutils.makeIdent(p,heapSym), treeutils.makeIntLiteral(p,1))); currentStatements.add(assign); wellDefinedCheck.clear(); } clearInvariants(); // FIXME - is this needed for rac? } protected JCIdent currentFresh = null; /** Helper method to do the work of visitApply and visitNewObject */ protected void applyHelper(JCExpression that) { // We need to save the context of many variables because in the case of // new object creation there may be nested methods that are converted // before we return to complete the translation of method declaration // we are now in (methodDecl) LinkedList<ListBuffer<JCStatement>> check0 = markBlock(); JCExpression savedCondition = condition; // This is the logical context in which this method is called - only used for JML expressions /*@ nullable */ Symbol savedResultSym = resultSym; // This is the symbol of the JCIdent representing the result of the method call, null if the method is void /*@ nullable */ JCExpression savedResultExpr = resultExpr; /*@ nullable */ Symbol savedExceptionSym = exceptionSym; // The symbol that holds the actifve exception (or null) // FIXME - doesnot get changed so why save it? /*@ nullable */ JCIdent savedThisId = currentThisId; // The JCIdent holding what 'this' means in the current context (already translated) /*@ nullable */ JCExpression savedThisExpr = currentThisExpr; // The JCIdent holding what 'this' means in the current context (already translated) Map<Object,JCExpression> savedParamActuals = paramActuals; // Mapping from formal parameter symbols to the actual arguments // A map in which to save paramActuals for each overridden method Map<Symbol,JCVariableDecl> savedpreparams = preparams; ListBuffer<JCStatement> savedOldStatements = oldStatements; JCIdent savedFresh = currentFresh; JCIdent savedPreLabel = preLabel; Symbol savedEnclosingMethod = enclosingMethod; Symbol savedEnclosingClass = enclosingClass; Map<TypeSymbol,Type> savedTypeVarMapping = typevarMapping; Map<TypeSymbol,Type> newTypeVarMapping = typevarMapping; Name savedOldLabel = defaultOldLabel; applyNesting++; Map<Symbol,Map<Object,JCExpression>> mapParamActuals = new HashMap<Symbol,Map<Object,JCExpression>>(); // Set to true later if the callee is a super(...) call boolean isSuperCall = false; // Set to true later if the callee is a this(...) call boolean isThisCall = false; /*@ nullable */ JCVariableDecl exceptionDeclCall = translatingJML && esc ? null : treeutils.makeVarDef(syms.exceptionType, exceptionNameCall, methodDecl.sym, that.pos); try { // Translate the method name, and determine the receiver for the method call JCIdent newThisId = null; // The translated receiver JCExpression newThisExpr = null; // The translated receiver List<JCExpression> trArgs; // The translated arguments List<JCExpression> typeargs; // The translated type arguments /*@ nullable*/ JCExpression meth = null; // the qualified method name, if this is a method call /*@ nullable*/ JCMethodInvocation apply = null; // non-null if this is a method call /*@ nullable*/ JCNewClass newclass = null; // non-null if this a new object call MethodSymbol calleeMethodSym = null; // The method symbol of the callee method or constructor if (that instanceof JCMethodInvocation) { apply = (JCMethodInvocation)that; meth = apply.meth; trArgs = apply.args; typeargs = apply.typeargs; } else if (that instanceof JCNewClass) { newclass = (JCNewClass) that; trArgs = newclass.args; typeargs = newclass.typeargs; } else { error(that,"Invalid argument type for JmlAssertionAdder.applyHelper"); return; } // FIXME - do we need to convert the varargsElement or its associated expressions // Convert all the expressions // There is duplicate code because we have to be sure to evaluate everything in order JCExpression trExpr = null; // Holds the translated method call (for RAC) Type receiverType; if (meth instanceof JCIdent) { receiverType = currentThisExpr != null ? currentThisExpr.type : classDecl.type; JCIdent id = (JCIdent)meth; if (utils.isJMLStatic(id.sym)) meth = convertExpr(meth); isSuperCall = id.name.equals(names._super); isThisCall = id.name.equals(names._this); typeargs = convert(typeargs); trArgs = convertArgs(that, trArgs,meth.type.asMethodType().argtypes, (id.sym.flags() & Flags.VARARGS) != 0 ); calleeMethodSym = (MethodSymbol)id.sym; newTypeVarMapping = typevarMapping = typemapping(receiverType, calleeMethodSym, typeargs); JCMethodInvocation mExpr = M.at(that).Apply(typeargs,meth,trArgs); mExpr.setType(that.type); mExpr.varargsElement = null; // We have combined the arargs elements into an array trExpr = mExpr; newThisExpr = utils.isJMLStatic(id.sym) ? null : splitExpressions ? newTemp(currentThisExpr) : currentThisExpr; newThisId = newThisExpr instanceof JCIdent ? (JCIdent)newThisExpr : null; enclosingMethod = id.sym; enclosingClass = id.sym.owner; } else if (meth instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)meth; receiverType = fa.selected.type; newTypeVarMapping = typevarMapping = typemapping(receiverType, fa.sym, null); JCExpression convertedReceiver = convertExpr(fa.selected); if (!utils.isJMLStatic(fa.sym)) { if (splitExpressions) { newThisExpr = newTemp(convertedReceiver); newThisId = (JCIdent)newThisExpr; } else { newThisExpr = convertedReceiver; newThisId = null; } if (translatingJML && (fa.selected instanceof JCIdent && localVariables.containsKey(((JCIdent)fa.selected).sym))) { // Local variables are presumed non-null } else { if (javaChecks) { // Check that receiver is not null JCExpression e = treeutils.makeNotNull(fa.selected.pos,newThisExpr); addAssert(fa, translatingJML? Label.UNDEFINED_NULL_DEREFERENCE : Label.POSSIBLY_NULL_DEREFERENCE,e); } } } typeargs = convert(typeargs); trArgs = convertArgs(that, trArgs,meth.type.asMethodType().argtypes, (fa.sym.flags() & Flags.VARARGS) != 0); JCFieldAccess fameth = (JCFieldAccess)M.at(meth.pos).Select( !utils.isJMLStatic(fa.sym) ? newThisExpr : convertedReceiver, fa.sym); calleeMethodSym = (MethodSymbol)fa.sym; JCMethodInvocation mExpr = M.at(that).Apply(typeargs,fameth,trArgs); mExpr.setType(that.type); mExpr.varargsElement = null; // We have combined the arargs elements into an array trExpr = mExpr; // rewritten expression - for RAC enclosingMethod = fa.sym; enclosingClass = fa.sym.owner; } else if (newclass != null) { // FIXME - this does not handle quantified constructors of inner classes calleeMethodSym = (MethodSymbol)newclass.constructor; enclosingMethod = calleeMethodSym; enclosingClass = calleeMethodSym.owner; JCExpression convertedReceiver = convertExpr(newclass.encl); receiverType = newclass.clazz.type; newTypeVarMapping = typevarMapping = typemapping(newclass.clazz.type, null, null); if (javaChecks && convertedReceiver != null && !treeutils.isATypeTree(convertedReceiver)) { // Check that receiver is not null JCExpression e = treeutils.makeNotNull(newclass.encl.pos,convertedReceiver); addAssert(newclass.encl, translatingJML? Label.UNDEFINED_NULL_DEREFERENCE : Label.POSSIBLY_NULL_DEREFERENCE,e); } typeargs = convert(typeargs); trArgs = convertArgs(that, trArgs,calleeMethodSym.type.asMethodType().argtypes, (calleeMethodSym.flags() & Flags.VARARGS) != 0); JCNewClass expr = M.at(that).NewClass( convertedReceiver, typeargs, convert(newclass.clazz), trArgs, convert(newclass.def)); expr.constructor = newclass.constructor; expr.constructorType = newclass.constructorType; expr.varargsElement = null; // We have combined the arargs elements into an array expr.setType(that.type); trExpr = expr; // newThisId, newThisExpr are assigned the resultId later - can only be used in post-conditions } else { error(that,"Unknown alternative in interpreting method call"); return; } // Collect all the methods overridden by the method being called java.util.List<Pair<MethodSymbol,Type>> overridden = parents(calleeMethodSym,receiverType); /** We can either try to keep subexpressions as subexpressions, or break * them out into statements with temporary variables. Java code is * broken into statements so that side-effects can be handled straightforwardly. * Quantified expressions have to be kept as sub-expressions. * Other JML expressions can be handled either way - the 'doTranslations' * variable indicates what mode we are in: true means expand subexpresions * We currently expand non-quantified JML statements because * method calls in JML expressions are easier to handle. */ boolean uma = useMethodAxioms // // FIXME - org.jmlspecs.JML has special functions - we want to inline them, not represent as methods; could do this test more efficiently && !calleeMethodSym.owner.getQualifiedName().toString().equals(Strings.jmlSpecsPackage + ".JML") && calleeMethodSym.params != null; // // FIXME - Strings give lots of problems if used as functions with axioms - sort it out later // && !calleeMethodSym.owner.getQualifiedName().toString().equals("java.lang.String") // && !calleeMethodSym.owner.getQualifiedName().toString().equals("java.lang.CharSequence") ; boolean doTranslations = rac || !translatingJML || (!uma && localVariables.isEmpty()); if (!doTranslations && that instanceof JCNewClass) doTranslations = true; // FIXME - work this out in more detail. At least there should not be anonymous classes in JML expressions boolean calleeIsFunction = attr.isFunction(calleeMethodSym); if (calleeIsFunction && translatingJML) doTranslations = false; if (!doTranslations) { List<JCExpression> ntrArgs = trArgs; if (useMethodAxioms || !localVariables.isEmpty() || calleeIsFunction) { boolean details = true && !calleeMethodSym.owner.getQualifiedName().toString().equals(Strings.JMLClass) // && !calleeMethodSym.owner.getQualifiedName().toString().equals("java.lang.String") // && !calleeMethodSym.owner.getQualifiedName().toString().equals("java.lang.CharSequence") ; currentThisId = newThisId; currentThisExpr = newThisExpr; // First check all invariants // FIXME - use addInvariants? if (details && !isHelper(calleeMethodSym) && !startInvariants(calleeMethodSym.owner,that)) { for (Type t: parents(calleeMethodSym.owner.type, false)) { ClassSymbol csym = (ClassSymbol)t.tsym; for (JmlTypeClause clause: specs.getSpecs(csym).clauses) { if (clause.token != JmlTokenKind.INVARIANT) continue; if (utils.isJMLStatic(calleeMethodSym) && !utils.isJMLStatic(clause.modifiers,csym)) continue; if (!utils.jmlvisible(null,classDecl.sym, csym, clause.modifiers.flags, methodDecl.mods.flags)) continue; JCExpression e = convertJML(((JmlTypeClauseExpr)clause).expression); e = treeutils.makeImplies(condition.pos, condition, e); if (assumingPureMethod) { addAssume(that,Label.UNDEFINED_PRECONDITION,e, clause,clause.source()); } else if (!addingAxioms) { // FIXME - these asserts end up in the wrong spot for axioms, but what guards do we need? addAssert(that,Label.UNDEFINED_PRECONDITION,e, clause,clause.source()); } } } endInvariants(calleeMethodSym.owner); } if (!utils.isJMLStatic(calleeMethodSym)) { ntrArgs = ntrArgs.prepend(newThisExpr); } if (!attr.hasAnnotation(calleeMethodSym,JmlTokenKind.FUNCTION)) { JCExpression heap = treeutils.makeIdent(that.pos,heapSym); ntrArgs = ntrArgs.prepend(heap); // only if heap dependent } JCBlock bl = addMethodAxioms(that,calleeMethodSym,overridden); if (details) { // FIXME - document this details check - if it is false, the axioms are dropped // FIXME - actually should add these into whatever environment is operative if (inOldEnv) { escAddToOldList(oldenv,bl); } else if (nonignoredStatements != null) { nonignoredStatements.add(bl); } else if (axiomBlock != null) { axiomBlock.stats = axiomBlock.stats.append(bl); } else { addStat(bl); } WellDefined info = wellDefinedCheck.get(calleeMethodSym); if (info != null && !info.alltrue) { // FIXME - should not ever be null? perhaps anon types? MethodSymbol s = info.sym; if (s != null && localVariables.isEmpty() && !treeutils.isTrueLit(info.wellDefinedExpression)) { JCExpression e = treeutils.makeMethodInvocation(that,null,s,convertCopy(ntrArgs)); e = treeutils.makeImplies(condition.pos, condition, e); if (assumingPureMethod) { addAssume(that,Label.UNDEFINED_PRECONDITION,e, info.pos,info.source); } else { addAssert(that,Label.UNDEFINED_PRECONDITION,e, info.pos,info.source); } } } } MethodSymbol newCalleeSym = pureMethod.get(calleeMethodSym); if (newCalleeSym == null) { log.error("jml.internal","No logical function for method " + calleeMethodSym.getQualifiedName()); } result = eresult = treeutils.makeMethodInvocation(that,null,newCalleeSym,ntrArgs); } else { if (utils.isJMLStatic(calleeMethodSym)) { result = eresult = trExpr; } else { result = eresult = treeutils.makeMethodInvocation(that,newThisExpr,calleeMethodSym,trArgs); } } return; } // Set up the result variable - for RAC we have to do this // outside the block that starts a few lines down, because the // result is a temporary ident that is used in subsequent // expressions - at least if the method returns a value and the // method call is a subexpression of a larger expression Type resultType = null; if (meth != null) resultType = meth.type.getReturnType(); if (newclass != null) resultType = newclass.clazz.type; boolean isVoid = resultType.getTag() == TypeTag.VOID; // A variable for the method call result is declared, and then // everything else is within a block. JCIdent resultId = null; if (!isVoid) { if (esc && !doTranslations) { result = eresult = trExpr; // FIXME - for now skip all the checking of preconditions etc - we are in the middle of a quantified expression return; } else if (rac) { if (resultType instanceof Type.CapturedType) { resultType = ((Type.CapturedType)resultType).getUpperBound(); } resultSym = resultType.tsym; resultId = currentFresh = newTempNull(that,resultType); // initialized to null } else if (newclass == null) { if (resultType instanceof Type.CapturedType) { resultType = ((Type.CapturedType)resultType).getUpperBound(); } resultSym = resultType.tsym; resultId = newTemp(that,resultType); } else { Type t = that.type; if (t instanceof Type.TypeVar) t = paramActuals.get(t.toString()).type; JCVariableDecl decl = treeutils.makeVarDef(t,names.fromString(Strings.newObjectVarString + that.pos), null, that.pos); addStat(decl); resultSym = decl.sym; resultId = treeutils.makeIdent(that.pos, decl.sym); } } resultExpr = resultId; if (newclass != null) { newThisId = resultId; newThisExpr = resultExpr; // FIXME - what about newclass.encl addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeNeqObject(that.pos, convertCopy(resultId), treeutils.nullLit)); } if (!translatingJML && calleeIsFunction) { // FIXME - replicated from above JCBlock bl = addMethodAxioms(that,calleeMethodSym,overridden); if (true) { // FIXME - document this details check - if it is false, the axioms are dropped // FIXME - actually should add these into whatever environment is operative if (inOldEnv) { escAddToOldList(oldenv,bl); } else if (nonignoredStatements != null) { nonignoredStatements.add(bl); } else if (axiomBlock != null) { axiomBlock.stats = axiomBlock.stats.append(bl); } else { addStat(bl); } WellDefined info = wellDefinedCheck.get(calleeMethodSym); if (info != null && !info.alltrue) { // FIXME - should not ever be null? perhaps anon types? MethodSymbol s = info.sym; if (s != null && localVariables.isEmpty() && !treeutils.isTrueLit(info.wellDefinedExpression)) { JCExpression e = treeutils.makeMethodInvocation(that,null,s,convertCopy(trArgs)); e = treeutils.makeImplies(condition.pos, condition, e); if (assumingPureMethod) { addAssume(that,Label.UNDEFINED_PRECONDITION,e, info.pos,info.source); } else { addAssert(that,Label.UNDEFINED_PRECONDITION,e, info.pos,info.source); } } } } MethodSymbol newCalleeSym = pureMethod.get(calleeMethodSym); if (newCalleeSym == null) { log.error("jml.internal","No logical function for method " + calleeMethodSym.getQualifiedName()); } JCExpression methCall = treeutils.makeMethodInvocation(that,null,newCalleeSym,trArgs); JCStatement stat = treeutils.makeAssignStat(that.pos, resultExpr, methCall); addStat(stat); } // Add nullness and type assumptions about the result, for new operations // FIXME - what about for method calls // FIXME - rac should have the option of checking these assumptions if (esc && newclass != null) { // Result of a new operation is not null addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeNotNull(that.pos, convertCopy(resultExpr))); // Type of result equals the type of the class of the new operation // FIXME - what about anonymous types JmlMethodInvocation typeof = M.at(that).JmlMethodInvocation(JmlTokenKind.BSTYPEOF, convertCopy(resultExpr)); typeof.javaType = false; typeof.type = jmltypes.TYPE; JmlMethodInvocation stype = M.at(that).JmlMethodInvocation(JmlTokenKind.BSTYPELC, treeutils.makeType(that.pos, newclass.type)); stype.javaType = false; stype.type = jmltypes.TYPE; addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeEqObject(that.pos, typeof, stype)); // Java type of the result equals the java type of the new operation // FIXME - what about anonymous types typeof = M.at(that).JmlMethodInvocation(JmlTokenKind.BSTYPEOF, convertCopy(resultExpr)); typeof.javaType = true; typeof.type = jmltypes.TYPE; stype = M.at(that).JmlMethodInvocation(JmlTokenKind.BSTYPELC, treeutils.makeType(that.pos, newclass.type)); stype.javaType = true; stype.type = jmltypes.TYPE; addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeEqObject(that.pos, typeof, stype)); if (resultExpr != null && !resultExpr.type.isPrimitive()) { newAllocation1(that,resultExpr); // FIXME - should have an JCIdent? JCFieldAccess fa = treeutils.makeSelect(that.pos, resultExpr, allocSym); JCBinary e = treeutils.makeEquality(that.pos, fa, treeutils.makeIntLiteral(that.pos, ++allocCounter)); addAssume(that,Label.IMPLICIT_ASSUME,e); } } // FIXME - comment? // FIXME - don't duplicate what is in visitLabelled ListBuffer<JCStatement> check1 = pushBlock(); // FIXME - needs a try block? Name calllabel = null; if (!translatingJML) { JCBlock bl = M.at(that).Block(0L, List.<JCStatement>nil()); String label = "_JMLCALL_" + that.pos + "_" + (++count); calllabel = names.fromString(label); JCLabeledStatement stat = (M.at(that).Labelled(calllabel,bl)); addStat(stat); preLabel = M.at(that).Ident(calllabel); labelOldLists.put(calllabel, currentStatements); oldStatements = currentStatements; defaultOldLabel = calllabel; markLocation(calllabel,currentStatements,stat); } ListBuffer<JCStatement> saved = currentStatements; oldStatements = currentStatements; // FIXME - why twice ClassSymbol calleeClass = (ClassSymbol)calleeMethodSym.owner; // Before the actual method call, check caller invariants and the invariants of the caller's parameters // FIXME - the check on helper here is only if callee and caller have the same receiver, or is it receivers with the same class? if (!isHelper(calleeMethodSym) && !methodDecl.sym.isConstructor() && applyNesting <= 1) { addStat(comment(that, "Checking caller invariants before calling method " + utils.qualifiedMethodSig(calleeMethodSym),null)); if (!isSuperCall && !isThisCall) { if (meth instanceof JCFieldAccess) { addInvariants(that,calleeClass.type, utils.isJMLStatic(calleeMethodSym) || calleeMethodSym.isConstructor() ? null : newThisExpr, currentStatements, false,calleeMethodSym.isConstructor(),isSuperCall,isHelper(calleeMethodSym),false,false,Label.INVARIANT_EXIT_CALLER, "(Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"); //utils.qualifiedMethodSig(methodDecl.sym) + " " + utils.qualifiedMethodSig(calleeMethodSym)); // FIXME - do we really do post here and below } } // Note that methodDecl.params will be null for initializer blocks if (methodDecl.params != null) for (JCVariableDecl v: methodDecl.params) { if (v.type.isPrimitive()) continue; // FIXME - it is an open question which invariants to check here - in principle all invariants must hold - but which might not? - need the pack/unpack capability // FIXME - for now we check the invariants of the parameters in the prestate JCVariableDecl d = preparams.get(v.sym); JCIdent id = treeutils.makeIdent(v.pos,d.sym); addStat(comment(v, "Checking invariants for caller parameter " + v.sym + " before calling method " + utils.qualifiedMethodSig(calleeMethodSym),null)); addInvariants(v,v.type,id,currentStatements, false,false,false,false,false,false,Label.INVARIANT_EXIT_CALLER, "(Parameter: " + v.sym + ", Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"); } } JCExpression collectedInvariants = treeutils.trueLit; // FIXME - do we need this - do we include this in the 'condition' ? if (!isSuperCall && !isThisCall && !isHelper(calleeMethodSym)) { // Iterate through parent classes and interfaces, adding relevant invariants String msg = "(Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"; addStat(comment(that, "Checking callee invariants by the caller " + utils.qualifiedMethodSig(methodDecl.sym) + " before calling method " + utils.qualifiedMethodSig(calleeMethodSym),null)); addInvariants(that,calleeClass.type,newThisExpr,currentStatements, false,calleeMethodSym.isConstructor(),false,isHelper(calleeMethodSym),false,false,Label.INVARIANT_ENTRANCE,msg); for (JCExpression arg: trArgs) { if (arg.type.isPrimitive()) continue; currentStatements.add(comment(arg, "Asserting invariants for callee parameter before calling the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); JCIdent id; if (arg instanceof JCIdent) id = (JCIdent)arg; else { continue; // FIXME - see testbigint } addInvariants(arg,arg.type,id,currentStatements, false,false,false,false,false,false,Label.INVARIANT_ENTRANCE,msg); } } // The following assumptions are to state that non-primitive arguments of a constructor call // cannot be equal to the newly constructed object. We don't check this for RAC because // at this point the value of the new object is still null, and might be equal to a null // argument. // FIXME - why don't the stipulations about allocation time suffice? if (calleeMethodSym.isConstructor() && esc) { for (JCExpression arg: trArgs) { if (arg.type.isPrimitive() || jmltypes.isJmlType(arg.type)) continue; JCExpression neq = treeutils.makeNeqObject(arg.pos,arg,newThisExpr); addAssume(arg, Label.IMPLICIT_ASSUME,neq); } } currentThisId = newThisId; currentThisExpr = newThisExpr; // A map to hold the preconditions, indexed by specification case Map<JmlSpecificationCase,JCExpression> preExpressions = new HashMap<JmlSpecificationCase,JCExpression>(); // Collects the complete precondition over all specification cases JCExpression combinedPrecondition = treeutils.falseLit; // Identify the clause to point to as the associated declaration if the precondition fails. // The combinedPrecondition may be an OR of many specification cases, // each of which may be an AND of clauses - so if the precondition is // false, every specification case is false. JmlMethodClauseExpr clauseToReference = null; boolean anyFound = false; for (Pair<MethodSymbol,Type> pair: overridden) { MethodSymbol mpsym = pair.first; JmlMethodSpecs s = specs.getDenestedSpecs(mpsym); if (s != null && !s.cases.isEmpty()) { anyFound = true; break; } } if (!anyFound) { // No specs - set default JmlSpecs.MethodSpecs s = specs.getSpecs(calleeMethodSym); if (s == null) { JmlMethodSpecs defaults = JmlSpecs.defaultSpecs(context,methodDecl.sym,methodDecl.pos).cases; s = new JmlSpecs.MethodSpecs(methodDecl.mods,defaults); specs.putSpecs(calleeMethodSym, s); s.cases.deSugared = defaults; } else { JCMethodDecl decl = s.cases.decl; JmlMethodSpecs defaults = JmlSpecs.defaultSpecs(context, decl == null ? null : decl.sym, // FIXME - why is decl ever null? decl == null ? methodDecl.pos : decl.pos).cases; s.cases = defaults; s.cases.deSugared = defaults; } } // Iterate over all specs to find preconditions, with the callee // method itself last { // FIXME - what do we do with calls in quantifications boolean combinedNoModel = false; addStat(comment(that, "Checking preconditions of callee " + calleeMethodSym + " by the caller",null)); for (Pair<MethodSymbol,Type> pair: overridden) { MethodSymbol mpsym = pair.first; Type classType = pair.second; // FIXME - meth is null for constructors - fix that also; also generic types typevarMapping = typemapping(classType, calleeMethodSym, typeargs, meth == null ? null : meth.type instanceof Type.MethodType ? (Type.MethodType)meth.type : null); // This initial logic must match that below for postconditions JmlMethodSpecs calleeSpecs = specs.getDenestedSpecs(mpsym); if (calleeSpecs == null) continue; // FIXME - not sure about this - should get a default? paramActuals = new HashMap<Object,JCExpression>(); mapParamActuals.put(mpsym,paramActuals); if (calleeSpecs.decl != null) { Iterator<JCVariableDecl> iter = calleeSpecs.decl.params.iterator(); JCVariableDecl currentDecl = null; for (JCExpression arg: trArgs) { if (iter.hasNext()) currentDecl = iter.next(); paramActuals.put(currentDecl.sym, arg); } } if (esc) { if (newclass != null && newclass.clazz instanceof JCTypeApply) { Iterator<Symbol.TypeVariableSymbol> tpiter = calleeClass.getTypeParameters().iterator(); // calleeSpecs.decl.typarams.iterator(); for (JCExpression tp: ((JCTypeApply)newclass.clazz).arguments ) { paramActuals.put(tpiter.next(), tp); } } if (newclass == null && ( typeargs == null || typeargs.isEmpty())) { Type.MethodType t = null; if (calleeMethodSym.type instanceof Type.MethodType) { t = (Type.MethodType)calleeMethodSym.type; } else if (calleeMethodSym.type instanceof Type.ForAll) { t = ((Type.ForAll)calleeMethodSym.type).asMethodType(); } List<Type> list = t.argtypes; Iterator<Type> tpiter = list.iterator(); for (Type tp: ((Type.MethodType)meth.type).argtypes ) { Type tt = tpiter.next(); if (tt instanceof Type.TypeVar) { paramActuals.put(tt.toString(), treeutils.makeType(that.pos, tp)); } } } if (typeargs != null && !typeargs.isEmpty()) { Iterator<Symbol.TypeVariableSymbol> tpiter = calleeMethodSym.getTypeParameters().iterator(); for (JCExpression tp: typeargs ) { paramActuals.put(tpiter.next(), tp); } } } // FIXME - should this really be before the preconditions computations - it does have to be before the assignable computations. if (esc && (!specs.isPure(calleeMethodSym) || newclass != null) && resultId != null && !resultType.isPrimitive()) { if (esc && resultId != null && !resultType.isPrimitive()) { JCTree cs = that; // JCFieldAccess x = (JCFieldAccess)M.at(cs.pos).Select(null,isAllocSym); // JCStatement havoc = M.at(cs.pos).JmlHavocStatement(List.<JCExpression>of(x)); // addStat(havoc); // havoc *.isAllocSym { // JCVariableDecl d = newTempDecl(cs, syms.objectType); // JCIdent id = treeutils.makeIdent(cs.pos, d.sym); // JCExpression eold = treeutils.makeOld(cs.pos, treeutils.makeSelect(cs.pos, id, isAllocSym), // oldenv); // id = treeutils.makeIdent(cs.pos, d.sym); // JCExpression enew = treeutils.makeSelect(cs.pos, id, isAllocSym); // JCExpression f = M.at(cs).JmlQuantifiedExpr(JmlToken.BSFORALL, List.<JCVariableDecl>of(d), eold, enew); // addAssume(cs,Label.IMPLICIT_ASSUME,f); // addAssume(cs,Label.IMPLICIT_ASSUME,treeutils.makeNot(cs.pos, treeutils.makeSelect(cs.pos, resultId, isAllocSym))); } // if (!specs.isPure(calleeMethodSym) || newclass != null) { // newAllocation2(that,resultId); // } } } // For each specification case, we accumulate the precondition // and we create a block that checks the assignable statements for (JmlSpecificationCase cs : calleeSpecs.cases) { if (!utils.visible(classDecl.sym, mpsym.owner, cs.modifiers.flags/*, methodDecl.mods.flags*/)) continue; if (translatingJML && cs.token == JmlTokenKind.EXCEPTIONAL_BEHAVIOR) continue; if (mpsym != calleeMethodSym && cs.code) continue; JCIdent preId = null; if (rac) { preId = newTemp(treeutils.falseLit); pushBlock(); } JCExpression pre = convertCopy(translatingJML ? savedCondition : treeutils.trueLit); JCExpression prex = null; boolean noModel = false; JavaFileObject prev = log.useSource(cs.source()); try { JmlMethodClauseExpr mcc = null; // Remember the first clause in the specification case for (JmlMethodClause clause : cs.clauses) { switch (clause.token) { case OLD: { // FIXME - ignore if after all requires? // FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions // JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause; // olddecl = convertCopy(olddecl); // for (JCVariableDecl d: olddecl.decls) { // d.init = treeutils.makeOld(d.init,d.init); // } // scan(olddecl); break; } case FORALL: notImplemented(clause,"forall clause in method specs",clause.source()); break; case REQUIRES: JmlMethodClauseExpr mce = (JmlMethodClauseExpr)clause; // JCExpression e = convertJML(mce.expression,pre,false); // Might throw an exception // pre = pre == treeutils.trueLit ? e : treeutils.makeAnd(pre.pos, pre, e); prex = prex == null ? mce.expression : treeutils.makeAnd(mce.expression.pos, prex, mce.expression); if (mcc == null) mcc = mce; break; default: } } if (mcc != null) clauseToReference = mcc; pre = prex == null ? treeutils.trueLit : convertJML(prex,pre,false); } catch (NoModelMethod e) { pre = treeutils.falseLit; noModel = true; combinedNoModel = true; } catch (JmlNotImplementedException e) { notImplemented("requires clause containing ",e); // FIXME - clause source pre = treeutils.falseLit; } finally { log.useSource(prev); } if (!rac) { pre = newTemp(pre); } else if (noModel) { popBlock(); //pre = preId; // FIXME - should we say the the case is not being checked because ther eis a missing model method } else { // FIXME - not sure why this fails for esc (see rac-guarded statements above as well) preId.pos = pre.pos; addStat(treeutils.makeAssignStat(pre.pos,convertCopy(preId),pre)); addStat(wrapRuntimeException(pre,popBlock(0L,pre), "JML undefined precondition - exception thrown", null)); pre = preId; } preExpressions.put(cs,pre); // Add to the list of spec cases, in order of declaration combinedPrecondition = combinedPrecondition == treeutils.falseLit ? pre : pre == treeutils.falseLit ? combinedPrecondition : treeutils.makeOr(pre.pos, combinedPrecondition, pre); if ((!translatingJML || rac) && methodDecl != null && methodDecl.sym != null) { // FIXME - not quite sure of this guard // FIXME - what should we check for field initializers // Handle assignable & accessible clauses ListBuffer<JCStatement> check2 = pushBlock(); // A block for assignable and accessible tests boolean anyAssignableClauses = false; boolean anyCallableClauses = false; boolean anyAccessibleClauses = false; for (JmlMethodClause clause : cs.clauses) { // We iterate over each storeref item in each assignable clause // of each specification case of the callee - for each item we check // that assigning to it (under the appropriate preconditions) // is allowed by each of the specification cases of the caller specs. try { if (clause.token == JmlTokenKind.OLD) { JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause; olddecl = convertCopyNoEmit(olddecl); for (JCVariableDecl d: olddecl.decls) { d.init = treeutils.makeOld(d.init.pos,d.init,preLabel); } scan(olddecl); } else if (clause.token == JmlTokenKind.FORALL) { notImplemented(clause,"forall clause in method specs",clause.source()); } else if (clause.token == JmlTokenKind.ASSIGNABLE) { List<JCExpression> storerefs = expandStoreRefList(((JmlMethodClauseStoreRef)clause).list,calleeMethodSym); for (JCExpression item: storerefs) { addStat(comment(item,"Is " + item + " assignable? " + utils.locationString(item.pos,clause.source()),clause.source())); checkAgainstCallerSpecs(clause.token, that, item ,pre, savedThisId, newThisId, clause.source()); } anyAssignableClauses = true; } else if (clause.token == JmlTokenKind.ACCESSIBLE) { List<JCExpression> storerefs = expandStoreRefList(((JmlMethodClauseStoreRef)clause).list,calleeMethodSym); for (JCExpression item: storerefs) { addStat(comment(item,"Is " + item + " accessible?",clause.source())); checkAgainstCallerSpecs(clause.token, that, item ,pre, savedThisId, newThisId, clause.source()); } anyAccessibleClauses = true; } else if (clause.token == JmlTokenKind.CALLABLE) { anyCallableClauses = true; JmlMethodClauseCallable callableClause = (JmlMethodClauseCallable)clause; if (callableClause.keyword != null) { if (callableClause.keyword.token == JmlTokenKind.BSNOTHING){ // callee is callable \nothing - no problem } else if (callableClause.keyword.token == JmlTokenKind.BSEVERYTHING) { checkThatMethodIsCallable(callableClause.keyword, null); } else if (callableClause.keyword.token == JmlTokenKind.BSNOTSPECIFIED) { checkThatMethodIsCallable(callableClause.keyword, null); } } else { List<JmlMethodSig> sigs = callableClause.methodSignatures; if (sigs != null) { for (JmlMethodSig item: sigs) { addStat(comment(item,"Is " + item + " callable?",clause.source())); checkThatMethodIsCallable(item, item.methodSymbol); } } } } } catch (NoModelMethod e) { // continue // FIXME - warn? } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ",e); // FIXME - clause source } } if (!anyAssignableClauses) { // If there are no assignable clauses in the spec case, use a default if (mpsym.isConstructor()) { // But the default for a constructor call the fields of the constructor // and the fields of the constructor are allowed to be assigned in any case // So there is nothing to check // for (JCExpression item: defaultStoreRefs(cs,mpsym)) { // checkAgainstCallerSpecs(that, convertJML(item),pre, savedThisId, newThisId); // } } else { if (isPure(calleeMethodSym)) { checkAgainstCallerSpecs(JmlTokenKind.ASSIGNABLE, that, M.at(cs).JmlStoreRefKeyword(JmlTokenKind.BSNOTHING),pre, savedThisId, newThisId, cs.source()); } else { // the default is \\everything checkAgainstCallerSpecs(JmlTokenKind.ASSIGNABLE, that, M.at(cs).JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING),pre, savedThisId, newThisId, cs.source()); } } } if (!anyAccessibleClauses) { // If there are no assignable clauses in the spec case, use a default // if (mpsym.isConstructor()) { // But the default for a constructor call the fields of the constructor // and the fields of the constructor are allowed to be assigned in any case // So there is nothing to check // for (JCExpression item: defaultStoreRefs(cs,mpsym)) { // checkAgainstCallerSpecs(that, convertJML(item),pre, savedThisId, newThisId); // } // } else { // the default is \\everything checkAgainstCallerSpecs(JmlTokenKind.ACCESSIBLE, that, M.at(cs).JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING),pre, savedThisId, newThisId, cs.source()); // } } if (!anyCallableClauses) { // The callee is implicitly callable \everything, // so the caller must be also be implicitly callable \everything checkThatMethodIsCallable(cs, null); } // if (!anyAccessibleClauses) { // // If there are no accessible clauses in the spec case, use a default // if (mpsym.isConstructor()) { // // But the default for a constructor call the fields of the constructor // // and the fields of the constructor are allowed to be assigned in any case // // So there is nothing to check // //// for (JCExpression item: defaultStoreRefs(cs,mpsym)) { //// checkAgainstCallerSpecs(that, convertJML(item),pre, savedThisId, newThisId); //// } // // } else { // // the default is \\everything // checkAgainstCallerSpecs(JmlToken.ACCESSIBLE, that, M.at(cs).JmlStoreRefKeyword(JmlToken.BSEVERYTHING),pre, savedThisId, newThisId); // } // } JCBlock bl = popBlock(0,cs,check2); // Ending the assignable tests block if (!bl.stats.isEmpty()) addStat( M.at(cs).If(pre, bl, null) ); } } paramActuals = null; } if (clauseToReference != null) { ListBuffer<JCStatement> check3 = pushBlock(); if (combinedNoModel) { // skip } else if (translatingJML) { //JCExpression conj = treeutils.makeAnd(methodDecl.pos, collectedInvariants, combinedPrecondition); addAssert(that,Label.UNDEFINED_PRECONDITION, treeutils.makeImplies(methodDecl.pos, condition, combinedPrecondition), clauseToReference,clauseToReference.source()); } else { addAssert(that,Label.PRECONDITION, combinedPrecondition, clauseToReference,clauseToReference.source()); } JCBlock bl = popBlock(0,that,check3); addStat( wrapRuntimeException(that, bl, "JML undefined precondition - exception thrown", null)); } } ListBuffer<JCStatement> ensuresStatsOuter = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> exsuresStatsOuter = new ListBuffer<JCStatement>(); // Now put in the actual method call // For esc, the resultSym is used in the postconditions; there is // no actual call of the method. Similarly for new object expressions. // FIXME - do need to state something about the allocation of the result of the new object expression typevarMapping = newTypeVarMapping; if (rac) { ListBuffer<JCStatement> s = currentStatements; currentStatements = ensuresStatsOuter; if (apply != null) addStat(comment(apply,"converted method call",null)); if (newclass != null) addStat(comment(newclass,"converted new-object call",null)); JCStatement call; if (newclass != null) { addStat(treeutils.makeAssignStat(that.pos, resultId, trExpr)); trExpr = resultId; // currentThisId = newThisId = resultId; // currentThisExpr = newThisExpr = resultId; // addAssume(that,Label.IMPLICIT_ASSUME, // treeutils.makeNeqObject(that.pos,resultId,treeutils.nullLit)); } else if (isVoid) { call = M.at(that).Exec(trExpr); addStat(call); } else { call = treeutils.makeAssignStat(that.pos, resultId, trExpr); addStat(call); } currentStatements = s; } ensuresStatsOuter.add(comment(that,"Assuming callee normal postconditions",null)); exsuresStatsOuter.add(comment(that,"Assuming callee exceptional postconditions",null)); if (doTranslations) { if (exceptionSym != null && exceptionDeclCall != null) { exsuresStatsOuter.add( treeutils.makeAssignStat( that.pos, treeutils.makeIdent(that.pos, exceptionSym), treeutils.makeIdent(that.pos, exceptionDeclCall.sym))); } // TERMINATION // The termination symbol will be null if a method call is present in class-level declaration // FIXME - not sure what precisely should be done - should we declare a termination symbol in this case? RAC probably needs it. if (terminationSym != null) { JCIdent tid = treeutils.makeIdent(that.pos,terminationSym); JCStatement term = treeutils.makeAssignStat(that.pos,tid,treeutils.makeIntLiteral(that.pos,-that.pos)); exsuresStatsOuter.add(term); } { // This states the allowed types of any exception according to the // Java declaration if (exceptionSym != null) { // FIXME - what situation has this symbol null? ListBuffer<JCStatement> s = currentStatements; currentStatements = exsuresStatsOuter; if (specs.isPure(calleeMethodSym)) { addAssume(that, Label.IMPLICIT_ASSUME,treeutils.falseLit); } else { JCIdent exceptionId = treeutils.makeIdent(that.pos,exceptionSym); JCExpression expr = treeutils.makeThrownPredicate(that, exceptionId, calleeMethodSym); addAssume(that, Label.IMPLICIT_ASSUME,expr); } currentStatements = s; } } } if (esc) { // Now we iterate over all specification cases in all parent // methods again, this time putting in the assignable havoc statements for (Pair<MethodSymbol,Type> pair: overridden) { MethodSymbol mpsym = pair.first; Type classType = pair.second; typevarMapping = typemapping(classType, null, null); // This initial logic must match that above for preconditions JmlMethodSpecs calleeSpecs = specs.getDenestedSpecs(mpsym); if (calleeSpecs == null) continue; // FIXME - not sure about this paramActuals = mapParamActuals.get(mpsym); boolean isPure = isPure(mpsym); // FIXME - we should set condition // Be sure to do assignable (havoc) clauses before the invariant and postcondition clauses for (JmlSpecificationCase cs : calleeSpecs.cases) { if (!utils.visible(classDecl.sym, mpsym.owner, cs.modifiers.flags/*, methodDecl.mods.flags*/)) continue; if (translatingJML && cs.token == JmlTokenKind.EXCEPTIONAL_BEHAVIOR) continue; if (mpsym != calleeMethodSym && cs.code) continue; JCExpression pre = convertCopy(preExpressions.get(cs)); if (pre == treeutils.falseLit) continue; // Don't bother with postconditions if corresponding precondition is explicitly false condition = pre; // FIXME - is this right? what about the havoc statement? ListBuffer<JCStatement> check4 = pushBlock(); boolean useDefault = true; for (JmlMethodClause clause : cs.clauses) { try { JmlTokenKind token = clause.token; switch (token) { case OLD: { JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause; olddecl = convertCopyNoEmit(olddecl); for (JCVariableDecl d: olddecl.decls) { d.init = treeutils.makeOld(d.init,d.init); } scan(olddecl); break; } case FORALL: notImplemented(clause,"forall clause in method specs",clause.source()); break; case ASSIGNABLE: // Don't translate assignable if we are in a pure method or constructor if (!translatingJML) { useDefault = false; addStat(comment(clause)); ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); List<JCExpression> storerefs = expandStoreRefList(((JmlMethodClauseStoreRef)clause).list,calleeMethodSym); for (JCExpression location: storerefs) { location = convertAssignable(location,newThisId,true); if (location instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)location; if (fa.sym == null) { JCExpression e = fa.selected; boolean isStatic = treeutils.isATypeTree(e); for (VarSymbol v: utils.listJmlVisibleFields(e.type.tsym, calleeMethodSym.flags()&Flags.AccessFlags, isStatic)) { JCFieldAccess newfa = treeutils.makeSelect(location.pos, e, v); JCExpression trfa= convertAssignable(newfa,newThisId,true); newlist.add(trfa); } } else if (isModel(fa.sym)){ JCExpression e = fa.selected; boolean isStatic = treeutils.isATypeTree(e); for (VarSymbol v: utils.listJmlVisibleFields(e.type.tsym, Flags.PRIVATE, isStatic)) { if (isContainedIn(v,fa.sym)) { JCFieldAccess newfa = treeutils.makeSelect(location.pos, e, v); JCExpression trfa= convertAssignable(newfa,newThisId,true); newlist.add(trfa); } } } else { newlist.add(location); continue; } } else if (location instanceof JmlStoreRefArrayRange) { JmlStoreRefArrayRange loc = (JmlStoreRefArrayRange)location; if (loc.lo == loc.hi && loc.lo != null) { //Just an array access JCExpression receiver = newThisId; JCExpression index = convertAssignable(loc.lo,receiver,true); JCExpression array = convertAssignable(loc.expression,receiver,true); JmlBBArrayAccess newloc = new JmlBBArrayAccess(null,array,index); // FIXME - switch to factory newloc.pos = loc.pos; newloc.setType(loc.type); newloc.arraysId = null; newlist.add(newloc); } else { //log.error("jml.internal","not implemented: assignable translation for array ranges"); newlist.add(location); } } else { newlist.add(location); } } JCStatement havoc = M.at(clause.pos).JmlHavocStatement(newlist.toList()); addStat(havoc); } break; default: // skip everything else break; } } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); } } if (useDefault && !translatingJML) { if (newclass == null) { // default for non- constructor call is \everything JCStatement havoc = M.at(cs.pos).JmlHavocStatement(List.<JCExpression>of(M.at(cs.pos).JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING))); addStat(havoc); } else { // default for constructor call is this.* JCStatement havoc = M.at(cs.pos).JmlHavocStatement(defaultStoreRefs(that,(MethodSymbol)newclass.constructor)); addStat(havoc); } } JCBlock bl = popBlock(0,cs,check4); JCStatement st = M.at(cs.pos+1).If(pre,bl,null); bl = M.at(cs.pos+1).Block(0,List.<JCStatement>of(st)); currentStatements.add( wrapRuntimeException(cs, bl, "JML undefined precondition while checking postconditions - exception thrown", null)); // if (esc && (!specs.isPure(calleeMethodSym) || newclass != null) && resultId != null && !resultType.isPrimitive()) { // JCFieldAccess x = (JCFieldAccess)M.at(cs.pos).Select(null,isAllocSym); // JCStatement havoc = M.at(cs.pos).JmlHavocStatement(List.<JCExpression>of(x)); // addStat(havoc); // havoc *.isAllocSym // { // JCVariableDecl d = newTempDecl(cs, syms.objectType); // JCIdent id = treeutils.makeIdent(cs.pos, d.sym); // JCExpression eold = treeutils.makeOld(cs.pos, treeutils.makeSelect(cs.pos, id, isAllocSym), // oldenv); // id = convertCopy(id); // JCExpression enew = treeutils.makeSelect(cs.pos, id, isAllocSym); // JCExpression f = M.at(cs).JmlQuantifiedExpr(JmlToken.BSFORALL, List.<JCVariableDecl>of(d), eold, enew); // addAssume(cs,Label.IMPLICIT_ASSUME,f); // } // newAllocation2(that,resultId); // } // FIXME - is that the right statement list? } paramActuals = null; } } typevarMapping = newTypeVarMapping; if (newclass != null || (!specs.isPure(calleeMethodSym) && !calleeMethodSym.isConstructor())) { if (inProcessInvariants.isEmpty() && !translatingJML) changeState(); } if (doTranslations) { ListBuffer<JCStatement> check5 = pushBlock(); String msg = utils.qualifiedMethodSig(calleeMethodSym) + ", returning to " + utils.qualifiedMethodSig(methodDecl.sym); currentStatements.add(comment(that, "Assuming callee invariants by the caller " + utils.qualifiedMethodSig(methodDecl.sym) + " after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); // Assume non_null fields, since they might have been havoced // FIXME - not enabled for rc; they are just assumptions (which could // usefully be checked), but they sappear to include model fields. if (!rac) { addNonNullChecks(true, that, calleeClass.type, newThisExpr, calleeMethodSym.isConstructor()); } if (!isHelper(calleeMethodSym)) addInvariants(that,calleeClass.type,newThisExpr,currentStatements, false,calleeMethodSym.isConstructor(),false,isHelper(calleeMethodSym),true,true,Label.INVARIANT_EXIT, msg); addConstraintInitiallyChecks(that,calleeClass,newThisExpr,currentStatements, false,calleeMethodSym.isConstructor(),false,isHelper(calleeMethodSym),true,true,null, msg); if (!isHelper(calleeMethodSym)) for (JCExpression arg: trArgs) { if (arg.type.isPrimitive()) continue; currentStatements.add(comment(arg, "Assuming invariants for callee parameter after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); if (!(arg instanceof JCIdent)) continue; // FIXME - do better? - see testbigint JCIdent id = (JCIdent)arg; addInvariants(id,arg.type,id,currentStatements, false,false,false,false,true,true,Label.INVARIANT_EXIT,msg); } Type retType = calleeMethodSym.getReturnType(); if (calleeMethodSym.isConstructor()) { // FIXME - invariants for constructor result - already somewhere else? } else if (retType.getTag() != TypeTag.VOID) { // Add invariants on the type of the return value only if normal termination ListBuffer<JCStatement> check6 = pushBlock(); if (esc && !retType.isPrimitive()) { JCExpression nn = treeutils.makeEqObject(that.pos, resultId, treeutils.nullLit); nn = treeutils.makeOr(that.pos, nn, isAllocated(that,resultId)); addAssume(that,Label.IMPLICIT_ASSUME,nn); nn = treeutils.makeDynamicTypeInEquality(that, resultId, retType); addAssume(that, Label.IMPLICIT_ASSUME, nn); } currentStatements.add(comment(that, "Assuming invariants for the return value by the caller after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); boolean savedAPM = assumingPureMethod; assumingPureMethod = true; addInvariants(that,retType,resultId,currentStatements, false,false,false,false,true,true,Label.INVARIANT_EXIT, msg); assumingPureMethod = savedAPM; JCBlock bl = popBlock(0,that,check6); if (exceptionSym == null) { currentStatements.add(bl); } else { JCIdent exceptionId = treeutils.makeIdent(that.pos,exceptionSym); JCExpression e = treeutils.makeEqObject(that.pos,exceptionId,treeutils.nullLit); JCStatement ifstat = M.at(that.pos).If(e,bl,null); currentStatements.add(ifstat); } } // Now assume that the callee has maintained all the caller invariants // both explicit invariants and invariants of the classes of the parameters if (!isSuperCall && !isThisCall && !isHelper(calleeMethodSym) && !specs.isPure(calleeMethodSym)) { currentStatements.add(comment(that, "Assuming caller invariants upon reentering the caller " + utils.qualifiedMethodSig(methodDecl.sym) + " after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); addInvariants(that,savedEnclosingClass.type, savedEnclosingMethod == null || utils.isJMLStatic(savedEnclosingMethod) ? null : savedThisExpr, currentStatements, true,savedEnclosingMethod != null && savedEnclosingMethod.isConstructor(),isSuperCall,isHelper(methodDecl.sym),false,true,Label.INVARIANT_REENTER_CALLER, "(Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"); // Note that methodDecl.params will be null for initalizer blocks if (methodDecl.params != null) for (JCVariableDecl v: methodDecl.params) { if (v.type.isPrimitive()) continue; // FIXME - it is an open question which invariants to check here - in principle all invariants must hold - but which might not? - need the pack/unpack capability // FIXME - for now we check the invariants of the parameters in the prestate JCVariableDecl d = preparams.get(v.sym); JCIdent id = treeutils.makeIdent(v.pos,d.sym); currentStatements.add(comment(that, "Assuming invariants for caller parameter " + id + " upon reentering the caller " + utils.qualifiedMethodSig(methodDecl.sym) + " after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); addInvariants(v,v.type,id,currentStatements, false,false,false,false,false,true,Label.INVARIANT_REENTER_CALLER, "(Parameter: " + v.sym + ", Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"); } } if (isSuperCall) { currentStatements.add(comment(that, "Assuming field invariants after super call: " + utils.qualifiedMethodSig(methodDecl.sym) + " after exiting the callee " + utils.qualifiedMethodSig(calleeMethodSym),null)); for (Type parentType : parents(calleeMethodSym.owner.type, false)) { Scope s = parentType.tsym.members(); for (Symbol sym: s.getElements()) { if (!(sym instanceof VarSymbol)) continue; if (sym.type.isPrimitive()) continue; // FIXME should be isJMLPrimitivie? DiagnosticPosition pos = that; // FIXME - is this a good position? JCExpression expr = treeutils.makeSelect(pos.getPreferredPosition(),currentThisExpr,sym); addInvariants(pos,sym.type,expr,currentStatements, false,false,true,false,true,true,Label.INVARIANT_REENTER_CALLER, "(Field: " + sym + ", Caller: " + utils.qualifiedMethodSig(methodDecl.sym) + ", Callee: " + utils.qualifiedMethodSig(calleeMethodSym) + ")"); } } } // FIXME - could optimize if the block is empty except comments JCBlock invariantBlock = popBlock(0,methodDecl,check5); // FIXME - why are these put in different statement lists? if (esc) currentStatements.add( wrapRuntimeException(that, invariantBlock, "JML undefined invariant while checking postconditions - exception thrown", null)); if (rac) ensuresStatsOuter.add( wrapRuntimeException(that, invariantBlock, "JML undefined invariant while checking postconditions - exception thrown", null)); } { if (esc && resultId != null && !resultId.type.isPrimitive()) { JCExpression nn = treeutils.makeEqNull(resultId.pos, convertCopy(resultId)); JCExpression ty = treeutils.makeTypeof(convertCopy(resultId)); JCExpression typ = treeutils.makeTypelc(treeutils.makeType(resultId.pos,resultId.type)); JCExpression inst = treeutils.makeSubtype(ty,typ); ListBuffer<JCStatement> s = currentStatements; currentStatements = ensuresStatsOuter; addAssume(resultId,Label.IMPLICIT_ASSUME,treeutils.makeOr(resultId.pos,nn,inst)); currentStatements = s; } // Now we iterate over all specification cases in all parent // methods again, this time putting in the post-condition checks for (Pair<MethodSymbol,Type> pair: overridden) { MethodSymbol mpsym = pair.first; Type classType = pair.second; typevarMapping = typemapping(classType, null, null); // This initial logic must match that above for preconditions JmlMethodSpecs calleeSpecs = specs.getDenestedSpecs(mpsym); if (calleeSpecs == null) continue; // FIXME - not sure about this ensuresStatsOuter.add(comment(methodDecl,"Assuming postconditions for " + utils.qualifiedMethodSig(mpsym),null)); exsuresStatsOuter.add(comment(methodDecl,"Assuming exceptional postconditions for " + utils.qualifiedMethodSig(mpsym),null)); paramActuals = mapParamActuals.get(mpsym); // FIXME - we should set condition // Be sure to do assignable (havoc) clauses, then invariants, and then postcondition clauses for (JmlSpecificationCase cs : calleeSpecs.cases) { //if (!utils.jmlvisible(null,classDecl.sym, classDecl.sym, cs.modifiers.flags, methodDecl.mods.flags)) continue; if (!utils.visible(classDecl.sym, mpsym.owner, cs.modifiers.flags/*, methodDecl.mods.flags*/)) continue; if (translatingJML && cs.token == JmlTokenKind.EXCEPTIONAL_BEHAVIOR) continue; if (mpsym != calleeMethodSym && cs.code) continue; ListBuffer<JCStatement> ensuresStats = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> exsuresStats = new ListBuffer<JCStatement>(); JCExpression pre = convertCopy(preExpressions.get(cs)); if (pre == treeutils.falseLit) continue; // Don't bother with postconditions if corresponding precondition is explicitly false condition = pre; // FIXME - is this right? what about the havoc statement? for (JmlMethodClause clause : cs.clauses) { JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile; JavaFileObject prevSource = null; try { switch (clause.token) { case OLD: { currentStatements = ensuresStats; JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause; olddecl = convertCopyNoEmit(olddecl); for (JCVariableDecl d: olddecl.decls) { d.init = treeutils.makeOld(d.init,d.init); } scan(olddecl); currentStatements = exsuresStats; olddecl = (JmlMethodClauseDecl)clause; olddecl = convertCopyNoEmit(olddecl); for (JCVariableDecl d: olddecl.decls) { d.init = treeutils.makeOld(d.init,d.init); } scan(olddecl); break; } case FORALL: notImplemented(clause,"forall clause in method specs",clause.source()); break; case ENSURES: currentStatements = ensuresStats; LinkedList<ListBuffer<JCStatement>> temp = markBlock(); ListBuffer<JCStatement> check7 = pushBlock(); try { addStat(comment(clause)); boolean savedAssuming = assumingPureMethod; try { prevSource = log.useSource(clauseSource); assumingPureMethod = true; JCExpression e = convertJML(((JmlMethodClauseExpr)clause).expression, condition, false); log.useSource(prevSource); addAssume(that,Label.POSTCONDITION,e,clause,clauseSource); } catch (NoModelMethod e) { // FIXME - need this elsewhere as well, e.g., signals // continue //} catch (Exception e) { // System.out.println(e); } finally { if (prevSource != null) log.useSource(prevSource); assumingPureMethod = savedAssuming; } JCBlock bl = popBlock(0,that,check7); addStat( wrapRuntimeException(clause, bl, // wrapRuntimeException is a no-op except for rac "JML undefined postcondition - exception thrown", null)); } catch (JmlNotImplementedException e) { popBlock(0,that,check7); notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); } if (!checkBlock(temp)) Utils.print("BLOCKS DO NOT MATCH"); break; case SIGNALS: // FIXME - review this currentStatements = exsuresStats; ListBuffer<JCStatement> check8 = pushBlock(); try { addStat(comment(clause)); JCExpression ex = ((JmlMethodClauseSignals)clause).expression; if (ex instanceof JmlSingleton) ex = treeutils.trueLit; JCVariableDecl vdo = ((JmlMethodClauseSignals)clause).vardef; Type vdtype = syms.exceptionType; if (vdo != null && !treeutils.isFalseLit(ex) && exceptionDeclCall != null) { JCIdent exceptionId = treeutils.makeIdent(clause.pos,exceptionDeclCall.sym); JCExpression tc = M.at(vdo).TypeCast(vdo.type, exceptionId); JCVariableDecl vd = treeutils.makeVarDef(vdo.type,vdo.name,vdo.sym.owner, esc ? exceptionId : tc); vdtype = vd.type; addStat(vd); paramActuals.put(vdo.sym, treeutils.makeIdent(vd.pos,vd.sym)); } // Should the condition be augmented with exception not null and of the right type? prevSource = log.useSource(clauseSource); JCExpression e = convertJML(ex, condition, false); log.useSource(prevSource); addAssume(that,Label.SIGNALS,e,clause,clauseSource); ex = treeutils.trueLit; if (vdo != null && !treeutils.isFalseLit(e) && exceptionDeclCall != null ) { ex = M.at(clause).TypeTest(treeutils.makeIdent(clause.pos, exceptionDeclCall.sym), treeutils.makeType(clause.pos, vdtype)).setType(syms.booleanType); paramActuals.remove(vdo.sym); } JCStatement st = M.at(clause).If(ex,popBlock(0,that,check8),null); addStat( wrapRuntimeException(clause, M.at(clause).Block(0,List.<JCStatement>of(st)), "JML undefined exceptional postcondition - exception thrown", null)); } catch (JmlNotImplementedException e) { popBlock(0,that,check8); notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); } finally { if (prevSource != null) log.useSource(prevSource); } break; case SIGNALS_ONLY: { currentStatements = exsuresStats; if (exceptionDeclCall != null && exceptionSym != null) { // FIXME - why might exceptionSym be null? halndling field initializer? JCIdent exceptionId = treeutils.makeIdent(clause.pos,exceptionSym); JCExpression condd = treeutils.falseLit; for (JCExpression t: ((JmlMethodClauseSignalsOnly)clause).list) { JCExpression tc = M.at(t).TypeTest(exceptionId, t).setType(syms.booleanType); condd = treeutils.makeOr(clause.pos, condd, tc); } addAssume(that,Label.SIGNALS_ONLY,condd,clause,clause.source(),null, treeutils.makeUtilsMethodCall(clause.pos,"getClassName",exceptionId)); } else { // FIXME - I think this should never happen // FIXME - shouldn'tx we include runtimeException JCExpression exx = treeutils.makeDuplicateLiteral(clause.pos,treeutils.falseLit); addAssume(that,Label.SIGNALS_ONLY,exx,clause,clauseSource); // FIXME - which exception } break; } case REQUIRES: case ASSIGNABLE: case ACCESSIBLE: case CALLABLE: break; default: // FIXME - implement others break; } } catch (NoModelMethod e) { // FIXME - need to catch to skip the clause } catch (JmlNotImplementedException e) { // FIXME - should not need this anymore notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); } } if (!ensuresStats.isEmpty()) { JCBlock ensuresBlock = M.at(cs.pos+1).Block(0, ensuresStats.toList()); // The +1 is so that the position of this if statement // and hence the names of the BRANCHT and BRANCHE variables // is different from the if prior to the apply // FIXME - review if this is still needed JCStatement st = M.at(cs.pos+1).If(pre,ensuresBlock,null); JCBlock bl = M.at(cs.pos+1).Block(0,List.<JCStatement>of(st)); ensuresStatsOuter.add( wrapRuntimeException(cs, bl, "JML undefined precondition while checking postconditions - exception thrown", null)); } if (!exsuresStats.isEmpty()) { JCBlock exsuresBlock = M.at(cs.pos+1).Block(0, exsuresStats.toList()); // The +1 is so that the position of this if statement // and hence the names of the BRANCHT and BRANCHE variables // is different from the if prior to the apply // FIXME - review if this is still needed JCStatement st = M.at(cs.pos+1).If(pre,exsuresBlock,null); JCBlock bl = M.at(cs.pos+1).Block(0,List.<JCStatement>of(st)); exsuresStatsOuter.add( wrapRuntimeException(cs, bl, "JML undefined precondition while checking exceptional postconditions - exception thrown", null)); } } paramActuals = null; } typevarMapping = newTypeVarMapping; } // FIXME - the source must be handled properly preExpressions.clear(); // Get rid of references - this may not really be needed, but it does not hurt { for (Symbol key: mapParamActuals.keySet()) { mapParamActuals.get(key).clear(); } mapParamActuals.clear(); mapParamActuals = null; } currentStatements = saved; if (exceptionDeclCall != null) { exsuresStatsOuter.add(M.at(that).Throw(treeutils.makeIdent(that.pos, exceptionDeclCall.sym))); } else if (rac) { System.out.println("DID NOT EXPECT THIS"); // FIXME } JCBlock ensuresBlock = M.at(that).Block(0, ensuresStatsOuter.toList()); if (rac) { JCBlock exsuresBlock = M.at(that).Block(0, exsuresStatsOuter.toList()); addStat( wrapException(that, ensuresBlock, exceptionDeclCall, exsuresBlock )); addStat( popBlock(0,methodDecl,check1) ); // Final outer block } else if (esc) { if (exceptionDeclCall != null) { // FIXME - what is happening if exceptionDeclCall is null - is the method pure? // declare the exception variable addStat(exceptionDeclCall); JCIdent nexceptionId = treeutils.makeIdent(that.getStartPosition(),exceptionDeclCall.sym); treeutils.copyEndPosition(nexceptionId, that); { JCStatement c = comment(that,"Exception thrown by " + (apply == null ? newclass.constructor : meth instanceof JCIdent ? ((JCIdent)meth).sym : ((JCFieldAccess)meth).sym),null); exsuresStatsOuter = exsuresStatsOuter.prepend(c); JCExpression nex = convertCopy(nexceptionId); pathMap.put(nex,c); exprBiMap.put(nex,nexceptionId); // log.noticeWriter.println("POSs " + nexceptionId.getStartPosition() // + " " + nexceptionId.getEndPosition(log.currentSource().getEndPosTable()) // + " " + nex.getStartPosition() // + " " + nex.getEndPosition(log.currentSource().getEndPosTable())); } JCBinary ch = treeutils.makeEqObject(that.pos, nexceptionId, treeutils.nullLit); JCBlock exsuresBlock = M.at(that).Block(0, exsuresStatsOuter.toList()); JCStatement st = M.at(that.pos).If(ch, ensuresBlock, exsuresBlock); addStat(st); } else { addStat(ensuresBlock); } addStat( popBlock(0,methodDecl,check1) ); // Final outer block } if (resultId != null) result = eresult = treeutils.makeIdent(resultId.pos, resultId.sym); else result = eresult = null; } finally { paramActuals = savedParamActuals; resultSym = savedResultSym; resultExpr = savedResultExpr; exceptionSym = savedExceptionSym; preparams = savedpreparams; currentThisId = savedThisId; currentThisExpr = savedThisExpr; oldStatements = savedOldStatements; condition = savedCondition; preLabel = savedPreLabel; defaultOldLabel = savedOldLabel; currentFresh = savedFresh; enclosingMethod = savedEnclosingMethod; enclosingClass = savedEnclosingClass; typevarMapping = savedTypeVarMapping; applyNesting--; checkBlock(check0); } } // FIXME - what about type arguments // 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.noticeWriter.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 (false) { // eresult = insertSpecMethodCall(that.pos,msym,obj,that.typeargs,newargs.toList()); // } else { // eresult = insertMethodCall(that,msym,obj,that.getTypeArguments(),newargs.toList()); // typeargs ? FIXME // } // //// popTypeArgs(); // // // // MethodSymbol msym = null; //// if (that.meth instanceof JCIdent) msym = (MethodSymbol)((JCIdent)that.meth).sym; //// else if (that.meth instanceof JCFieldAccess) msym = (MethodSymbol)((JCFieldAccess)that.meth).msym; //// else { //// //FIXME ERROR //// } //// JmlMethodSpecs calleeSpecs = JmlSpecs.instance(context).getDenestedSpecs(msym).deSugared; //// calleeSpecs. // JCExpression e = M.Apply(that.typeargs,method,newargs.toList()); // e.pos = that.pos; // e.setType(that.type); // if (that.type.getTag() != TypeTag.VOID) eresult = newTemp(e); // else eresult = e; // // TODO Auto-generated method stub // //throw new RuntimeException("Unexpected visit call in JmlAssertionAdder: " + that.getClass()); // } /** Returns true iff the only statements on the list are comment statements */ protected boolean onlyComments(Iterable<JCStatement> list) { for (JCStatement s: list) { if (!(s instanceof JmlStatementExpr && ((JmlStatementExpr)s).token == JmlTokenKind.COMMENT)) return false; } return true; } protected void newAllocation1(DiagnosticPosition pos, JCExpression expr) { int p = pos.getPreferredPosition(); JCFieldAccess fa = treeutils.makeSelect(p, convertCopy(expr), isAllocSym); addAssume(pos,Label.IMPLICIT_ASSUME,treeutils.makeNot(p, fa)); } protected void newAllocation2(DiagnosticPosition pos, JCIdent resultId) { int p = pos.getPreferredPosition(); // JCFieldAccess fa = treeutils.makeSelect(p, convertCopy(resultId), allocSym); // JCExpressionStatement st = treeutils.makeAssignStat(p, fa, treeutils.makeIntLiteral(p, ++allocCounter)); // addStat( st ); JCFieldAccess fa = treeutils.makeSelect(p, convertCopy(resultId), isAllocSym); JCStatement st = treeutils.makeAssignStat(p, fa, treeutils.makeBooleanLiteral(p,true)); addStat( st ); } protected void newAllocation(DiagnosticPosition pos, JCIdent resultId) { newAllocation1(pos,convertCopy(resultId)); newAllocation2(pos,convertCopy(resultId)); } // FIXME - review newClass @Override public void visitNewClass(JCNewClass that) { // FIXME - need to check definedness by testing preconditions when translatingJML JCExpression savedCondition = condition; condition = treeutils.trueLit; // FIXME - need to call the constructor; need an assertion about the type of the result; about allocation time boolean isUtils = that.type.toString().contains("org.jmlspecs.utils.Utils"); // FIXME - must be a better way if (pureCopy || isUtils) { ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); for (JCExpression arg: that.args) { args.add(convertExpr(arg)); } JCNewClass expr = M.at(that).NewClass( that.encl, convert(that.typeargs), convert(that.clazz), args.toList(), convert(that.def)); expr.constructor = that.constructor; expr.constructorType = that.constructorType; expr.varargsElement = that.varargsElement; expr.setType(that.type); result = eresult = expr; condition = savedCondition; return; } applyHelper(that); condition = savedCondition; } Map<Symbol, WellDefined> wellDefinedCheck = new HashMap<Symbol,WellDefined>(); /** Maps symbols of methods in Java or JML to the translated methods that * represent them in translated programs, and will be carried further into * the SMT translation. Such translations are only produced if the method's * specs are not inlined. The methods are always static. */ Map<Symbol,MethodSymbol> pureMethod = new HashMap<Symbol,MethodSymbol>(); Set<Symbol> axiomsAdded = new HashSet<Symbol>(); int heapCountForAxioms = -2; /** Returns true if the symbol was not already in the set for hc */ protected boolean addAxioms(int hc, Symbol sym) { if (hc != heapCountForAxioms) { axiomsAdded.clear(); pureMethod.clear(); wellDefinedCheck.clear(); heapCountForAxioms = hc; } return axiomsAdded.add(sym); } // FIXME - review newArray // OK @Override public void visitNewArray(JCNewArray that) { // Note that a JCNewArray can be the full new array expression: new Type[]{...} // But in a multi-dimensional initializer, each nested {...} is itself // a JCNewArray, though now with a null elemtype. ListBuffer<JCExpression> dims = new ListBuffer<JCExpression>(); for (JCExpression dim: that.dims) { JCExpression ex = convertExpr(dim); dims.add(ex); if (!pureCopy && ex != null) { JCBinary comp = treeutils.makeBinary(that.pos, JCTree.Tag.LE, treeutils.intleSymbol, treeutils.zero, ex); addAssert(that, translatingJML ? Label.UNDEFINED_NEGATIVESIZE : Label.POSSIBLY_NEGATIVESIZE, comp); } } ListBuffer<JCExpression> elems = new ListBuffer<JCExpression>(); if (that.elems != null) { for (JCExpression elem: that.elems) { elems.add(convertExpr(elem)); // FIXME - we need implicit conversions } } else { elems = null; } if (pureCopy) { JCExpression elemtype = convert(that.elemtype); result = eresult = M.at(that).NewArray(elemtype, dims.toList(), elems == null ? null: elems.toList()).setType(that.type); } else if (rac) { // This will create a fully-qualified version of the type name JCExpression elemtype = that.elemtype == null ? null : treeutils.makeType(that.elemtype.pos, that.elemtype.type); result = eresult = M.at(that).NewArray(elemtype, dims.toList(), elems == null ? null: elems.toList()).setType(that.type); result = eresult = newTemp(eresult); } else { // esc // FIXME - Creating a decl and initializing it outside the expression doe snot work for translatigJML if there is a quantifier expression JCVariableDecl decl = treeutils.makeVarDef(that.type,names.fromString(Strings.newArrayVarString + that.pos), null, that.pos); addStat(decl); JCIdent id = treeutils.makeIdent(that.pos, decl.sym); addArrayElementAssumptions(that, id, dims, elems); result = eresult = convertCopy(id); // FIXME - need assertions about size of array and about any known array elements; about allocation time // also about type of the array } } protected void addArrayElementAssumptions(DiagnosticPosition p, JCIdent array, ListBuffer<JCExpression> dims, ListBuffer<JCExpression> elems) { JCExpression size = null; int pos = p.getPreferredPosition(); if (dims != null && dims.length() > 0) { size = dims.first(); } else if (elems != null) { size = treeutils.makeIntLiteral(array.pos, elems.length()); } else { // ERROR } addAssume(array,Label.NULL_CHECK,treeutils.makeNeqObject(array.pos, array, treeutils.nullLit)); if (elems != null) { Type ctype = ((Type.ArrayType)array.type).getComponentType(); newAllocation(array,array); if (size != null) { addAssume(array,Label.IMPLICIT_ASSUME,treeutils.makeEquality(array.pos,treeutils.makeLength(array,convertCopy(array)),convert(size))); } int i = 0; for (JCExpression e: elems) { JCLiteral index = treeutils.makeIntLiteral(e.pos, i); e = addImplicitConversion(e,ctype,e); JmlBBArrayAccess aa = new JmlBBArrayAccess(null,array,index); // FIXME - switch to factory aa.pos = array.pos; aa.setType(ctype); aa.arraysId = null; JCBinary b = treeutils.makeEquality(e.pos,aa,e); addAssume(e.pos(),Label.IMPLICIT_ASSUME,b); ++i; } } else { if (dims.first() instanceof JCLiteral) { // JCExpression e = createNullInitializedArray(array.type, dims.toList()); // JCBinary b = treeutils.makeEquality(e.pos,array,e); // addAssume(array,Label.IMPLICIT_ASSUME,b); } else { newAllocation(array,array); if (size != null) { addAssume(array,Label.IMPLICIT_ASSUME,treeutils.makeEquality(array.pos,treeutils.makeLength(array,convertCopy(array)),convert(size))); } // FIXME - needs lots more implementation - quantified expressions } // addAssume(array,Label.NULL_CHECK,treeutils.makeNeqObject(array.pos, array, treeutils.nullLit)); // newAllocation(array,array); // if (size != null) { // addAssume(array,Label.IMPLICIT_ASSUME,treeutils.makeEquality(array.pos,treeutils.makeLength(array,convertCopy(array)),convert(size))); // } } JCExpression e3 = treeutils.makeDynamicTypeEquality(p, array, array.type); addAssume(p,Label.IMPLICIT_ASSUME,e3); if (true) { JCFieldAccess fa = treeutils.makeSelect(pos, convertCopy(array), allocSym); JCExpression e = treeutils.makeEquality(pos, fa, treeutils.makeIntLiteral(pos, ++allocCounter) ); addAssume(p,Label.IMPLICIT_ASSUME,e); if (dims.first() instanceof JCLiteral) { e = createNullInitializedArray(array.type, dims.toList()); JCBinary b = treeutils.makeEquality(e.pos,array,e); addAssume(array,Label.IMPLICIT_ASSUME,b); } else { int i = 0; ListBuffer<JCVariableDecl> decls = new ListBuffer<JCVariableDecl>(); JCExpression range = null; JCExpression expr = convertCopy(array); Type ct = array.type; for (JCExpression ex: dims) { JCVariableDecl vd = treeutils.makeIntVarDef(names.fromString("i"+ i), null, null); decls.add(vd); JCExpression ee = treeutils.makeBinary(pos, JCTree.Tag.LE, treeutils.intleSymbol, treeutils.makeIntLiteral(pos, 0), treeutils.makeIdent(pos, vd.sym)); JCExpression eee = treeutils.makeBinary(pos, JCTree.Tag.LT, treeutils.intltSymbol, treeutils.makeIdent(pos, vd.sym), convertCopy(ex)); ee = treeutils.makeAnd(pos, ee, eee); range = range == null ? ee : treeutils.makeAnd(pos, range, ee); // expr = M.at(p).Indexed(expr, treeutils.makeIdent(pos, vd.sym)); JmlBBArrayAccess aa = new JmlBBArrayAccess(null,expr, treeutils.makeIdent(pos, vd.sym)); // FIXME - switch to factory aa.setPos(p.getPreferredPosition()); ct = ((Type.ArrayType)ct).getComponentType(); aa.setType(ct); expr = aa; i++; } if (decls.size() > 0) { JCExpression q = M.at(p).JmlQuantifiedExpr(JmlTokenKind.BSFORALL, decls.toList(), range, treeutils.makeEquality(pos, expr, treeutils.makeZeroEquivalentLit(pos, ct))); q.setType(treeutils.falseLit.type); addAssume(array, Label.IMPLICIT_ASSUME, q); } } } } protected JCExpression createNullInitializedArray(Type type, List<JCExpression> dims) { DiagnosticPosition pos = methodDecl; int p = pos.getPreferredPosition(); if (!(type instanceof Type.ArrayType)) { return treeutils.makeZeroEquivalentLit(p,type); } else if (dims.head instanceof JCLiteral) { Type ctype = ((Type.ArrayType)type).getComponentType(); int sz = (Integer)((JCLiteral)dims.head).value; // FIXME - long? JCVariableDecl decl = newTempDecl(methodDecl, type); addStat(decl); JCIdent id = treeutils.makeIdent(p, decl.sym); addAssume(pos,Label.IMPLICIT_ASSUME, treeutils.makeNeqObject(p, id, treeutils.nullLit)); newAllocation(pos,id); addAssume(id,Label.IMPLICIT_ASSUME,treeutils.makeEquality(p,treeutils.makeLength(pos,convertCopy(id)),convertExpr(dims.head))); for (int i=0; i<sz; ++i) { JCLiteral index = treeutils.makeIntLiteral(p, i); JmlBBArrayAccess aa = new JmlBBArrayAccess(null,id,index); // FIXME - switch to factory aa.pos = p; aa.setType(ctype); aa.arraysId = null; JCExpression e = createNullInitializedArray(ctype, dims.tail); JCBinary b = treeutils.makeEquality(p,aa,e); addAssume(pos,Label.IMPLICIT_ASSUME,b); } return id; } else if (dims.head == null) { return treeutils.nullLit; } else { JCVariableDecl decl = newTempDecl(methodDecl, type); JCIdent id = treeutils.makeIdent(p, decl.sym); // id != null && id.length == 'dims.head' // (\forall int i; 0<=i && i < 'dims.head'; id[i] != null && TBD // FIXME - not implemented return id; } } // OK @Override public void visitParens(JCParens that) { JCExpression arg = convertExpr(that.getExpression()); result = eresult = M.at(that).Parens(arg).setType(arg.type); treeutils.copyEndPosition(eresult,that); } // FIXME - check endpos everywhere // FIXME - this needs work per the complicated Java conversion rules /** Adds any checks and conversions that Java implicitly applies to convert * the given expression to the given type. */ public JCExpression addImplicitConversion(DiagnosticPosition pos, Type newtype, JCExpression expr) { if (pureCopy) return expr; if (paramActuals != null && newtype instanceof Type.TypeVar) { JCExpression e = paramActuals.get(newtype.toString()); if (e != null) { newtype = e.type; } } boolean isPrim = expr.type.isPrimitive() && expr.type.getTag() != TypeTag.BOT; boolean newIsPrim = newtype.isPrimitive() && expr.type.getTag() != TypeTag.BOT; // If we are converting from a non-primitive to primitive type (unboxing), // check that the expression is not null // But we don't unbox for rac for JML types because those are represented as a non-primitive anyway if (javaChecks && newIsPrim && !isPrim && (esc || !jmltypes.isJmlType(newtype))) { JCExpression e = treeutils.makeNeqObject(pos.getPreferredPosition(), expr, treeutils.nullLit); if (translatingJML) { addAssert(pos, Label.UNDEFINED_NULL_UNBOX, treeutils.makeImplies(pos.getPreferredPosition(), condition, e)); } else { addAssert(pos, Label.POSSIBLY_NULL_UNBOX, e); } } if (rac) { if ((jmltypes.isSameType(newtype,jmltypes.BIGINT) || newtype.tsym == jmltypes.repSym(jmltypes.BIGINT)) && isPrim && !jmltypes.isJmlType(expr.type)) { return treeutils.makeUtilsMethodCall(expr.pos,"bigint_valueOf",expr); } else if ((jmltypes.isSameType(newtype,jmltypes.REAL) || newtype.tsym == jmltypes.repSym(jmltypes.REAL)) && isPrim && !jmltypes.isJmlType(expr.type)) { return treeutils.makeUtilsMethodCall(expr.pos,"real_valueOf",expr); } else if (expr.type.getKind() == TypeKind.NULL && newtype.getKind() != TypeKind.NULL) { expr = M.at(expr).TypeCast(newtype,expr); expr.type = newtype; return expr; // } else if (expr.type.isPrimitive() && newtype.isPrimitive() && expr.type.getTag() > newtype.getTag()) { // // TODO: understand - it appears that RAC does not like unnecessary casts - e.g. short to int // expr = M.at(expr).TypeCast(newtype,expr); // expr.type = newtype; // expr = newTemp(expr); // return expr; // - need to do casts explicitly at least for op= operations } else { return expr;// RAC handles implicit conversions implicitly } } if (types.isSameType(newtype,expr.type)) return expr; if (expr.type.getTag() == TypeTag.BOT || (expr instanceof JCLiteral && ((JCLiteral)expr).value == null)) return expr; // Type unboxed = unboxedType(expr.type); // int tag = unboxed.getTag(); // String methodName = // tag == TypeTag.INT ? "intValue" : // tag == TypeTag.SHORT ? "shortValue" : // tag == TypeTag.LONG ? "longValue" : // tag == TypeTag.BOOLEAN ? "booleanValue" : // tag == TypeTag.DOUBLE ? "doubleValue" : // tag == TypeTag.FLOAT ? "floatValue" : // tag == TypeTag.BYTE ? "byteValue" : // tag == TypeTag.CHAR ? "charValue" : ""; // ; // JCIdent id = M.Ident(names.fromString(methodName)); if (newIsPrim && !isPrim) { JCExpression mth = createUnboxingExpr(expr); if (translatingJML) { eresult = mth; } else { eresult = newTemp(mth); } isPrim = true; expr = eresult; if (types.isSameType(newtype,eresult.type)) return expr; } if (!newIsPrim && isPrim) { // boxing: Integer = int and the like JCExpression id = createBoxingStatsAndExpr(expr,newtype); eresult = id; } else { JCTypeCast t = M.at(pos).TypeCast(newtype,expr); t.clazz.type = newtype; t.type = newtype; eresult = t; // FIXME - for integer promotions, add assumptions about range of value } return eresult; } /** If the argument is a reference type with an unboxed version (e.g. Integer -> int) * then returns the unboxed type, otherwise returns the argument. */ protected Type unboxedType(Type t) { Type tt = types.unboxedType(t); if (tt == Type.noType) tt = t; return tt; } protected Type boxedType(Type t) { Type tt = types.boxedTypeOrType(t); return tt; } protected JCExpression createUnboxingExpr(JCExpression expr) { Type unboxed = unboxedType(expr.type); TypeTag tag = unboxed.getTag(); String methodName = tag == TypeTag.INT ? "intValue" : tag == TypeTag.SHORT ? "shortValue" : tag == TypeTag.LONG ? "longValue" : tag == TypeTag.BOOLEAN ? "booleanValue" : tag == TypeTag.DOUBLE ? "doubleValue" : tag == TypeTag.FLOAT ? "floatValue" : tag == TypeTag.BYTE ? "byteValue" : tag == TypeTag.CHAR ? "charValue" : ""; ; JCIdent id = M.Ident(names.fromString(methodName)); JCExpression mth = M.at(expr).JmlMethodInvocation(id, List.<JCExpression>of(expr)); mth.type = unboxed; return mth; } protected JCExpression createBoxingStatsAndExpr(JCExpression expr, Type newtype) { TypeTag tag = expr.type.getTag(); String methodName = tag == TypeTag.INT ? "intValue" : tag == TypeTag.SHORT ? "shortValue" : tag == TypeTag.LONG ? "longValue" : tag == TypeTag.BOOLEAN ? "booleanValue" : tag == TypeTag.DOUBLE ? "doubleValue" : tag == TypeTag.FLOAT ? "floatValue" : tag == TypeTag.BYTE ? "byteValue" : tag == TypeTag.CHAR ? "charValue" : ""; ; JCIdent id = M.Ident(names.fromString(methodName)); // T id JCIdent tmp = newTemp(expr.pos(), newtype); // the result - uninitialized id of type 'newtype' // assume id != null JCExpression e = treeutils.makeNeqObject(expr.getPreferredPosition(), tmp, treeutils.nullLit); addAssume(expr,Label.IMPLICIT_ASSUME,e); JmlMethodInvocation call = M.at(expr).JmlMethodInvocation(id, List.<JCExpression>of(tmp)); call.setType(expr.type); // Note: no symbol set, OK since this is esc e = treeutils.makeEquality(expr.pos,call,expr); addAssume(expr,Label.IMPLICIT_ASSUME,e); // assume addAssume(expr,Label.IMPLICIT_ASSUME, treeutils.makeDynamicTypeEquality(expr, treeutils.makeIdent(expr.getPreferredPosition(), tmp.sym), newtype)); return tmp; } // FIXME - document JCTree lastStat; protected void checkRW(JmlTokenKind rw, Symbol sym, JCExpression th, DiagnosticPosition p) { if (!translatingJML && methodDecl != null) { if (sym instanceof VarSymbol && sym.owner instanceof TypeSymbol) { Label lab = rw == JmlTokenKind.READABLE ? Label.READABLE : Label.WRITABLE; FieldSpecs fs = specs.getSpecs((VarSymbol)sym); if (fs != null) for (JmlTypeClause clause: fs.list) { if (clause.token == rw) { JCExpression ee = ((JmlTypeClauseConditional)clause).expression; JCExpression saved = currentThisExpr; try { currentThisExpr = th; ee = convertJML(ee); } finally { currentThisExpr = saved; } ee = makeAssertionOptional(ee); if (splitExpressions) { addAssert(p,lab,ee,clause,clause.source(),utils.qualifiedName(sym)); } else { addAssert(p,lab,treeutils.makeAnd(p.getPreferredPosition(),condition,ee),clause,clause.source(),utils.qualifiedName(sym)); } } } } } } protected boolean translatingLHS = false; // FIXME - review @Override public void visitAssign(JCAssign that) { if (pureCopy) { JCExpression lhs = convertExpr(that.lhs); JCExpression rhs = convertExpr(that.rhs); result = eresult = treeutils.makeAssign(that.pos, lhs, rhs); return; } if (that.lhs instanceof JCIdent) { JCIdent id = (JCIdent)that.lhs; translatingLHS = true; JCExpression lhs = convertExpr(that.lhs); translatingLHS = false; JCExpression rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that,lhs.type, rhs); Symbol.VarSymbol vsym = (Symbol.VarSymbol)id.sym; // specs.getSpecs(vsym). // FIXME - need to call isNonNull with the declaration for id if (specs.isNonNull(vsym)) { // && !(rac && vsym.type.tsym.toString().equals("org.jmlspecs.utils.IJMLTYPE"))) { JCExpression e = treeutils.makeNeqObject(that.pos, rhs, treeutils.nullLit); // FIXME - location of nnonnull declaration? addAssert(that, Label.POSSIBLY_NULL_ASSIGNMENT, e); } checkAccess(JmlTokenKind.ASSIGNABLE, that, that.lhs, lhs, currentThisId, currentThisId); checkRW(JmlTokenKind.WRITABLE,id.sym,currentThisExpr,id); JCExpressionStatement st = treeutils.makeAssignStat(that.pos, lhs, rhs); addStat( st ); lastStat = st.expr; result = eresult = lhs; exprBiMap.put(that, eresult); exprBiMap.put(that.lhs, eresult); if (!(lhs instanceof JCIdent)) { result = eresult = newTemp(lhs); exprBiMap.put(that.lhs, eresult); exprBiMap.put(that, eresult); } } else if (that.lhs instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)(that.lhs); JCFieldAccess newfa; if (!utils.isJMLStatic(fa.sym)) { JCExpression obj = convertExpr(fa.selected); JCExpression e = treeutils.makeNeqObject(obj.pos, obj, treeutils.nullLit); if (javaChecks) { addAssert(that.lhs, Label.POSSIBLY_NULL_DEREFERENCE, e); } checkRW(JmlTokenKind.WRITABLE,fa.sym,obj,fa); newfa = treeutils.makeSelect(that.pos, convertCopy(obj), fa.sym); //Map<JCTree,Integer> table = log.currentSource().getEndPosTable(); //log.noticeWriter.println("FA " + fa.getStartPosition() + " " + fa.getEndPosition(table) + " " + newfa.getStartPosition() + " " + newfa.getEndPosition(table)); } else { // We must evaluate the fa.lhs if it is an expression and not a type, // even though the value is not used (and might even be null) if (!treeutils.isATypeTree(fa.selected)) { scan(fa.selected); } // If the field is static, substitute the type tree JCExpression obj = treeutils.makeType(fa.getStartPosition(), fa.sym.owner.type); checkRW(JmlTokenKind.WRITABLE,fa.sym,obj,fa); newfa = treeutils.makeSelect(that.pos, obj, fa.sym); } JCExpression rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that,that.lhs.type, rhs); if (specs.isNonNull(fa.sym,methodDecl.sym.enclClass())) { JCExpression e = treeutils.makeNeqObject(fa.pos, rhs, treeutils.nullLit); // FIXME - location of nnonnull declaration? addAssert(that, Label.POSSIBLY_NULL_ASSIGNMENT, e); } // FIXME _ use checkAssignable checkAccess(JmlTokenKind.ASSIGNABLE, that, fa, newfa, currentThisId, currentThisId); // FIXME - should the second argument be newfa? // for (JmlSpecificationCase c: specs.getDenestedSpecs(methodDecl.sym).cases) { // JCExpression check = checkAssignable(fa,c,currentThisId.sym,currentThisId.sym); // if (!treeutils.isTrueLit(check)) { // DiagnosticPosition pos = c; // for (JmlMethodClause m : c.clauses) { // if (m.token == JmlToken.ASSIGNABLE) { pos = m; break; } // } // addAssert(that,Label.ASSIGNABLE,check,pos,c.sourcefile); // } // } JCExpressionStatement st = treeutils.makeAssignStat(that.pos, newfa, rhs); addStat( st ); lastStat = st.expr; exprBiMap.put(that, st.expr); result = eresult = newTemp(newfa); exprBiMap.put(that.lhs, eresult); } else if (that.lhs instanceof JCArrayAccess) { // that.lhs.getPreferredPosition() is the position of the [ in // the array access expression JCArrayAccess aa = (JCArrayAccess)(that.lhs); JCExpression array = convertExpr(aa.indexed); JCExpression e = treeutils.makeNeqObject(array.pos, array, treeutils.nullLit); if (javaChecks) addAssert(aa, Label.POSSIBLY_NULL_DEREFERENCE, e); JCExpression index = convertExpr(aa.index); index = addImplicitConversion(index,syms.intType,index); e = treeutils.makeBinary(index.pos, JCTree.Tag.GE, index, treeutils.zero); if (javaChecks) addAssert(aa, Label.POSSIBLY_NEGATIVEINDEX, e); JCFieldAccess newfa = treeutils.makeLength(array,array); e = treeutils.makeBinary(index.pos, JCTree.Tag.GT, newfa, index); if (javaChecks) addAssert(aa, Label.POSSIBLY_TOOLARGEINDEX, e); JCArrayAccess newfaa = M.at(that.pos).Indexed(array,index); newfaa.setType(that.type); // FIXME - test this checkAccess(JmlTokenKind.ASSIGNABLE, that, aa, newfaa, currentThisId, currentThisId); // for (JmlSpecificationCase c: specs.getDenestedSpecs(methodDecl.sym).cases) { // JCExpression check = checkAssignable(aa,c,currentThisId.sym,currentThisId.sym); // if (!treeutils.isTrueLit(check)) { // DiagnosticPosition pos = c; // for (JmlMethodClause m : c.clauses) { // if (m.token == JmlToken.ASSIGNABLE) { pos = m; break; } // } // addAssert(that,Label.ASSIGNABLE,check,pos,c.sourcefile); // } // } JCExpression rhs = convertExpr(that.rhs); rhs = addImplicitConversion(rhs,that.lhs.type,rhs); if (javaChecks && !((Type.ArrayType)array.type).getComponentType().isPrimitive()) { if (esc) { JCExpression rhstype = treeutils.makeTypeof(rhs); JCExpression lhselemtype = treeutils.makeJmlMethodInvocation(array,JmlTokenKind.BSELEMTYPE,jmltypes.TYPE, treeutils.makeTypeof(array)); JCExpression sta = treeutils.makeJmlMethodInvocation(array,JmlTokenKind.SUBTYPE_OF,syms.booleanType,rhstype,lhselemtype); JCExpression rhsnull = treeutils.makeEqNull(rhs.pos, rhs); addAssert(that, Label.POSSIBLY_BAD_ARRAY_ASSIGNMENT, treeutils.makeOr(sta.pos,rhsnull,sta)); } else if (rac) { // FIXME - I don't think this is checkable // JCExpression rhstype = treeutils.makeTypeof(rhs); // JCExpression lhselemtype = treeutils.makeJmlMethodInvocation(array,JmlToken.BSELEMTYPE,jmltypes.TYPE, // treeutils.makeTypeof(array)); // JCExpression sta = treeutils.makeJmlMethodInvocation(array,JmlToken.SUBTYPE_OF,syms.booleanType,rhstype,lhselemtype); // JCExpression rhsnull = treeutils.makeEqNull(rhs.pos, rhs); // addAssert(that, Label.POSSIBLY_BAD_ARRAY_ASSIGNMENT, treeutils.makeOr(sta.pos,rhsnull,sta)); } } JCArrayAccess lhs = new JmlBBArrayAccess(null,array,index); lhs.pos = aa.pos; lhs.type = aa.type; JCExpressionStatement st = treeutils.makeAssignStat(that.pos,lhs,rhs); addStat( st ); lastStat = st.expr; exprBiMap.put(that, st.expr); result = eresult = newTemp(lhs); exprBiMap.put(that.lhs, eresult); } else { error(that,"An unknown kind of assignment seen in JmlAssertionAdder: " + that.lhs.getClass()); } // Don't change heap state if the assignment is just to a local variable if (!(that.lhs instanceof JCIdent && ((JCIdent)that.lhs).sym.owner instanceof Symbol.MethodSymbol)) { changeState(); } } // OK @Override public void visitAssignop(JCAssignOp that) { if (pureCopy) { JCExpression lhs = convertExpr(that.lhs); JCExpression rhs = convertExpr(that.rhs); JCAssignOp a = M.at(that).Assignop(that.getTag(), lhs, rhs); a.operator = that.operator; a.setType(that.type); result = eresult = a; return; } visitAssignopHelper(that,false); } // OK protected void visitAssignopHelper(JCAssignOp that, boolean post) { JCExpression lhs = that.lhs; JCExpression rhs = that.rhs; JCTree.Tag op = that.getTag().noAssignOp(); boolean lhsscanned = false; if (lhs instanceof JCIdent) { // Note checkRW is called during the following convertExpr lhs = convertExpr(lhs); // This may no longer be a JCIdent (e.g. f may become this.f or T.f) lhsscanned = true; } // if (!lhs.type.isPrimitive() && rhs.type.isPrimitive()) { // // FIXME - this is still not quite right if either lhs or rhs needs implicit conversion to int or long // // FIXME - also this results in any computations in the lhs being performed twice // JCExpression lcast = M.at(lhs.pos).TypeCast(rhs.type, lhs).setType(rhs.type); // JCExpression newop = M.at(that.pos).Binary(op, lcast, rhs).setType(rhs.type); // JCExpression rcast = M.at(that.pos).TypeCast(lhs.type, newop).setType(lhs.type); // rcast = M.at(that.pos).Assign(lhs,rcast).setType(that.type); // result = eresult = convert(rcast); // return; // } Type maxJmlType = that.lhs.type; if (jmltypes.isJmlType(that.rhs.type)) maxJmlType = that.rhs.type; Type optype = treeutils.opType(that.lhs.type,that.rhs.type); if (lhs instanceof JCIdent) { // We start with: id = expr // We generate // ... statements for the subexpressions of lhs (if any) // ... statements for the subexpressions of expr // temp = lhs' op rhs' // lhs' = temp Type t = unboxedType(that.type); JCExpression lhsc = addImplicitConversion(lhs,optype,convertCopy(lhs)); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,optype,rhs); // or optype? addBinaryChecks(that, op, lhsc, rhs, maxJmlType); // if (jmltypes.isJmlType(maxJmlType)) { rhs = makeBin(that, op, that.getOperator(), lhsc , rhs, maxJmlType); // or lhsc // } else { // rhs = treeutils.makeBinary(that.pos,op,lhsc,rhs); // } treeutils.copyEndPosition(rhs, that); checkAccess(JmlTokenKind.ASSIGNABLE, that, lhs, lhs, currentThisId, currentThisId); checkRW(JmlTokenKind.WRITABLE,((JCIdent)lhs).sym,currentThisExpr,lhs); // Note that we need to introduce the temporary since the rhs contains // identifiers that may be captured by the lhs. - TODO - need an example? JCIdent id = newTemp(rhs); JCExpression newlhs = treeutils.makeIdent(lhs.pos,((JCIdent)lhs).sym); JCExpression nid = addImplicitConversion(id,lhs.type,id); JCExpressionStatement st = addStat(treeutils.makeAssignStat(that.getStartPosition(), newlhs, nid)); result = eresult = post ? newTemp(lhs) : newlhs; exprBiMap.put(that.lhs,eresult); lastStat = st.expr; } else if (lhs instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess)lhs; JCFieldAccess newfa; if (utils.isJMLStatic(fa.sym)) { if (!lhsscanned && !treeutils.isATypeTree(fa.selected)) convertExpr(fa.selected); JCExpression typetree = treeutils.makeType(fa.selected.pos, fa.sym.owner.type); if (!(that.lhs instanceof JCIdent)) checkRW(JmlTokenKind.READABLE,fa.sym,typetree,fa); checkRW(JmlTokenKind.WRITABLE,fa.sym,typetree,fa); newfa = treeutils.makeSelect(fa.selected.pos, typetree, fa.sym); newfa.name = fa.name; rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,optype,rhs); } else { JCExpression sel = lhsscanned ? fa.selected : convertExpr(fa.selected); newfa = M.at(lhs).Select(sel, fa.name); newfa.sym = fa.sym; newfa.type = fa.type; if (javaChecks) { JCExpression e = treeutils.makeNeqObject(lhs.pos, sel, treeutils.nullLit); addAssert(that.lhs, Label.POSSIBLY_NULL_DEREFERENCE, e); } if (!(that.lhs instanceof JCIdent)) checkRW(JmlTokenKind.READABLE,fa.sym,sel,fa); checkRW(JmlTokenKind.WRITABLE,fa.sym,sel,fa); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,optype,rhs); // if (specs.isNonNull(fa.sym,methodDecl.sym.enclClass())) { // JCExpression e = treeutils.makeNeqObject(fa.pos, rhs, treeutils.nullLit); // // FIXME - location of nnonnull declaration? // addAssert(that, Label.POSSIBLY_NULL_ASSIGNMENT, e); // } } addBinaryChecks(that,op,newfa,rhs,maxJmlType); checkAccess(JmlTokenKind.ASSIGNABLE, that, lhs, lhs, currentThisId, currentThisId); // We have to make a copy because otherwise the old and new JCFieldAccess share // a name field, when in fact they must be different JCFieldAccess newlhs = treeutils.makeSelect(newfa.pos,newfa.selected,newfa.sym); JCExpression newfac = addImplicitConversion(newfa,optype,newfa); // Note that we need to introduce the temporary since the rhs may contain // identifiers that will be captured by the lhs. (TODO - example?) rhs = treeutils.makeBinary(that.pos,op ,newfac,rhs); JCIdent id = newTemp(rhs); JCExpression idc = addImplicitConversion(id, newlhs.type, id); JCExpressionStatement st = addStat(treeutils.makeAssignStat(that.getStartPosition(), newlhs, idc)); treeutils.copyEndPosition(st, that); result = eresult = post ? idc : newTemp(newlhs); exprBiMap.put(that.lhs, eresult); lastStat = st.expr; } else if (lhs instanceof JCArrayAccess) { JCArrayAccess aa = (JCArrayAccess)lhs; JCExpression array = convertExpr(aa.indexed); if (javaChecks) { JCExpression e = treeutils.makeNeqObject(array.pos, array, treeutils.nullLit); // FIXME - location of nnonnull declaration? addAssert(that.lhs, Label.POSSIBLY_NULL_DEREFERENCE, e); } JCExpression index = convertExpr(aa.index); index = addImplicitConversion(index,syms.intType,index); if (javaChecks) { JCExpression e = treeutils.makeBinary(index.pos, JCTree.Tag.GE, index, treeutils.zero); addAssert(that.lhs, Label.POSSIBLY_NEGATIVEINDEX, e); } JCFieldAccess newfa = treeutils.makeLength(array, array); if (javaChecks) { JCExpression e = treeutils.makeBinary(index.pos, JCTree.Tag.LT, index, newfa); addAssert(that.lhs, Label.POSSIBLY_TOOLARGEINDEX, e); } checkAccess(JmlTokenKind.ASSIGNABLE, that, lhs, lhs, currentThisId, currentThisId); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,optype,rhs); JCExpression nlhs = new JmlBBArrayAccess(null,array,index); // FIXME - use factory nlhs.pos = aa.pos; nlhs.type = aa.type; nlhs = newTemp(nlhs); nlhs = addImplicitConversion(nlhs,optype,nlhs); addBinaryChecks(that,op,nlhs,rhs,optype); // Note that we need to introduce the temporary since the rhs contains // identifiers that will be captured by the lhs. (TODO _ example?) rhs = treeutils.makeBinary(that.pos,op,nlhs,rhs); JCIdent id = newTemp(rhs); JCExpression idc = addImplicitConversion(id, lhs.type, id); lhs = new JmlBBArrayAccess(null,array,index); // FIXME - use factory lhs.pos = aa.pos; lhs.type = aa.type; JCExpressionStatement st = addStat(treeutils.makeAssignStat(that.getStartPosition(), lhs, idc)); treeutils.copyEndPosition(st, that); result = eresult = post ? idc : newTemp(lhs); exprBiMap.put(that.lhs, eresult); lastStat = st.expr; } else { error(that,"Unexpected kind of AST in JmlAssertionAdder.visitAssignOp: " + that.getClass()); } // Don't change heap state if the assignment is just to a local variable if (!(that.lhs instanceof JCIdent && ((JCIdent)that.lhs).sym.owner instanceof Symbol.MethodSymbol)) { changeState(); } } // FIXME - check boxing conversions for ++ --? /** This translates Java unary operations: ++ -- ! ~ - + */ @Override public void visitUnary(JCUnary that) { JCTree.Tag tag = that.getTag(); if (pureCopy) { JCExpression arg = convertExpr(that.getExpression()); result = eresult = treeutils.makeUnary(that.pos,tag,that.getOperator(),arg); } else if (tag == JCTree.Tag.PREDEC || tag == JCTree.Tag.PREINC) { JCAssignOp b = M.at(that.getStartPosition()).Assignop(tag == JCTree.Tag.PREDEC ? JCTree.Tag.MINUS_ASG : JCTree.Tag.PLUS_ASG ,that.getExpression(),treeutils.one); treeutils.copyEndPosition(b, that); b.setType(that.type); Type t = that.type.getTag().ordinal() < TypeTag.INT.ordinal() ? syms.intType : unboxedType(that.type); b.operator = treeutils.findOpSymbol(tag == JCTree.Tag.PREDEC ? JCTree.Tag.MINUS : JCTree.Tag.PLUS , t); visitAssignopHelper(b,false); } else if (tag == JCTree.Tag.POSTDEC || tag == JCTree.Tag.POSTINC){ JCExpression arg = convertExpr(that.getExpression()); JCIdent id = newTemp(arg); JCAssignOp b = M.at(that).Assignop(tag == JCTree.Tag.POSTDEC ? JCTree.Tag.MINUS_ASG : JCTree.Tag.PLUS_ASG, that.getExpression() ,treeutils.one); treeutils.copyEndPosition(b, that); b.setType(that.type); Type t = that.type.getTag().ordinal() < TypeTag.INT.ordinal() ? syms.intType : unboxedType(that.type); b.operator = treeutils.findOpSymbol(tag == JCTree.Tag.POSTDEC ? JCTree.Tag.MINUS : JCTree.Tag.PLUS, t); visitAssignopHelper(b,true); exprBiMap.put(that.getExpression(),eresult); result = eresult = id; } else if (tag == JCTree.Tag.NEG || tag == JCTree.Tag.COMPL || tag == JCTree.Tag.PLUS) { result = eresult = currentArithmeticMode.rewriteUnary(this, that); if (splitExpressions) result = eresult = newTemp(eresult); } else { JCExpression arg = convertExpr(that.getExpression()); if (tag == JCTree.Tag.NOT && arg instanceof JCLiteral) { result = eresult = treeutils.makeBooleanLiteral(arg.pos, !((Boolean)((JCLiteral)arg).getValue())); // FIXME - other constant folding? } else { JCExpression e = treeutils.makeUnary(that.pos,tag,that.getOperator(),arg); if (splitExpressions) e = newTemp(e); result = eresult = e; } } } /** Add any assertions to check for problems with unary operations. */ public void addUnaryChecks(JCExpression that, int op, JCExpression expr) { // FIXME - add checks for numeric overflow } /** Add any assertions to check for problems with binary operations. */ public void addBinaryChecks(JCExpression that, JCTree.Tag op, JCExpression lhs, JCExpression rhs, Type maxJmlType) { if (op == JCTree.Tag.DIV || op == JCTree.Tag.MOD) { @Nullable JCExpression nonzero = nonZeroCheck(that,rhs); if (javaChecks && nonzero != null) addAssert(that,Label.POSSIBLY_DIV0,nonzero); } // NOTE: In Java, a shift by 0 is a no-op (aside from type promotion of the lhs); // a shift by a positive or negative amount s is actually a shift by // the amount (s & 31) for int lhs or (s & 63) for long lhs. // So any rhs value is legal, but may be unexpected. if (op == JCTree.Tag.SL || op == JCTree.Tag.SR || op == JCTree.Tag.USR) { int mask = (lhs.type.getTag() == TypeTag.LONG) ? 63 : 31; JCExpression expr = treeutils.makeBinary(that.pos, JCTree.Tag.BITAND, mask == 31 ? treeutils.intbitandSymbol : treeutils.longbitandSymbol, rhs, treeutils.makeIntLiteral(that.pos, mask)); expr = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, rhs, expr); addAssert(that,Label.POSSIBLY_LARGESHIFT,expr); } // FIXME - add a command-line switch to enable the above? // FIXME - add checks for numeric overflow } // FIXME - check that condition is never used when pureCopy == true // OK @Override public void visitBinary(JCBinary that) { // FIXME - check on numeric promotion, particularly shift operators JCTree.Tag tag = that.getTag(); boolean equality = tag == JCTree.Tag.EQ || tag == JCTree.Tag.NE; boolean comp = equality || tag == JCTree.Tag.GE || tag == JCTree.Tag.LE || tag == JCTree.Tag.LT || tag == JCTree.Tag.GT; boolean shift = tag == JCTree.Tag.SL || tag == JCTree.Tag.SR || tag == JCTree.Tag.USR; boolean arith = tag == JCTree.Tag.PLUS || tag == JCTree.Tag.MINUS || tag == JCTree.Tag.MUL || tag == JCTree.Tag.DIV || tag == JCTree.Tag.MOD; boolean bit = tag == JCTree.Tag.BITAND || tag == JCTree.Tag.BITOR || tag == JCTree.Tag.BITXOR; if (pureCopy) { JCExpression lhs = convertExpr(that.getLeftOperand()); JCExpression rhs = convertExpr(that.getRightOperand()); result = eresult = treeutils.makeBinary(that.pos,tag,that.getOperator(),lhs,rhs); } else if (tag == JCTree.Tag.PLUS && that.type.equals(syms.stringType)) { if (esc) { Symbol s = utils.findMember(syms.stringType.tsym,"concat"); if (s == null) log.error(that,"jml.internal","Could not find the concat method"); else { JCMethodInvocation call; JCExpression lhs = that.getLeftOperand(); JCExpression rhs = that.getRightOperand(); lhs = convertExpr(lhs); if (lhs.type.isPrimitive() && lhs.type.getTag() != TypeTag.BOT) { lhs = addImplicitConversion(that.getLeftOperand(),boxedType(lhs.type),lhs); if (lhs.type.tsym != syms.stringType.tsym) { Symbol tostring = utils.findMember(lhs.type.tsym,"toString"); if (tostring == null) log.error(that,"jml.internal","Could not find the concat method"); JCExpression meth = M.at(that).Select(lhs,tostring); lhs = M.at(lhs).Apply(null, meth, List.<JCExpression>nil()).setType(syms.stringType); } } rhs = convertExpr(rhs); if (rhs.type.isPrimitive() && rhs.type.getTag() != TypeTag.BOT) { rhs = addImplicitConversion(that.getRightOperand(),boxedType(rhs.type),rhs); if (rhs.type.tsym != syms.stringType.tsym) { Symbol tostring = utils.findMember(rhs.type.tsym,"toString"); if (tostring == null) log.error(that,"jml.internal","Could not find the concat method"); JCExpression meth = M.at(that).Select(rhs,tostring); rhs = M.at(rhs).Apply(null, meth, List.<JCExpression>nil()).setType(syms.stringType); } } JCExpression meth = M.at(that).Select(lhs,s); call = M.at(that).Apply(null, meth, List.<JCExpression>of(rhs)).setType(that.type); visitApply(call); } return; } else { // rac JCExpression lhs = convertExpr(that.getLeftOperand()); JCExpression rhs = convertExpr(that.getRightOperand()); result = eresult = treeutils.makeBinary(that.pos,tag,that.getOperator(),lhs,rhs); return; } } else if (tag == JCTree.Tag.AND || tag == JCTree.Tag.OR) { JCExpression prev = condition; try { Type maxJmlType = that.getLeftOperand().type; if (jmltypes.isJmlType(that.getRightOperand().type)) maxJmlType = that.getRightOperand().type; // FIXME - check that all the checks in makeBinaryCHecks are here - or somehow reuse that method here // same for unary checks if (tag == JCTree.Tag.AND) { if (splitExpressions) { JCConditional cond = M.at(that).Conditional(that.lhs,that.rhs,treeutils.falseLit); cond.setType(that.type); visitConditional(cond); return; } else { JCExpression lhs = convertExpr(that.getLeftOperand()); JCExpression rhs = that.getRightOperand(); lhs = addImplicitConversion(lhs,syms.booleanType,lhs); if (translatingJML) condition = treeutils.makeAnd(that.lhs.pos, condition, lhs); if (!(lhs instanceof JCLiteral && ((JCLiteral)lhs).getValue().equals(Boolean.FALSE))) { rhs = convertExpr(rhs); // condition is used within scanExpr so this statement must follow the previous one rhs = addImplicitConversion(rhs,syms.booleanType,rhs); if (translatingJML) adjustWellDefinedConditions(lhs); result = eresult = makeBin(that,tag,that.getOperator(),lhs,rhs,maxJmlType); } else { result = eresult = lhs; } } } else if (tag == JCTree.Tag.OR) { if (splitExpressions) { JCConditional cond = M.at(that).Conditional(that.lhs,treeutils.trueLit,that.rhs); cond.setType(that.type); visitConditional(cond); return; } else { JCExpression lhs = convertExpr(that.getLeftOperand()); JCExpression rhs = that.getRightOperand(); lhs = addImplicitConversion(lhs,syms.booleanType,lhs); if (translatingJML) condition = treeutils.makeAnd(that.lhs.pos, condition, treeutils.makeNot(that.lhs.pos,lhs)); if (!(lhs instanceof JCLiteral && ((JCLiteral)lhs).getValue().equals(Boolean.TRUE))) { rhs = convertExpr(rhs); // condition is used within scanExpr so this statement must follow the previous one rhs = addImplicitConversion(rhs,syms.booleanType,rhs); if (translatingJML) adjustWellDefinedConditions(treeutils.makeNot(that.lhs.pos,lhs)); result = eresult = makeBin(that,tag,that.getOperator(),lhs,rhs,maxJmlType); } else { result = eresult = lhs; } } // FIXME - add checks for numeric overflow - PLUS MINUS MUL } } finally { condition = prev; } } else if (arith) { result = eresult = currentArithmeticMode.rewriteBinary(this, that); if (splitExpressions) result = eresult = newTemp(eresult); } else if (translatingJML) { Type maxJmlType = that.getLeftOperand().type; boolean lhsIsPrim = that.getLeftOperand().type.isPrimitive() && that.getLeftOperand().type.getTag() != TypeTag.BOT; boolean rhsIsPrim = that.getRightOperand().type.isPrimitive() && that.getRightOperand().type.getTag() != TypeTag.BOT; if (jmltypes.isJmlType(that.getRightOperand().type)) maxJmlType = that.getRightOperand().type; if (lhsIsPrim && rhsIsPrim && that.getLeftOperand().type.getTag().ordinal() < that.getRightOperand().type.getTag().ordinal()) maxJmlType = that.getRightOperand().type; JCExpression lhs = convertExpr(that.getLeftOperand()); JCExpression rhs = that.getRightOperand(); // FIXME - check that all the checks in makeBinaryCHecks are here - or somehow reuse that method here // same for unary checks if (tag == JCTree.Tag.DIV || tag == JCTree.Tag.MOD) { lhs = addImplicitConversion(lhs,that.type,lhs); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,that.type,rhs); @Nullable JCExpression nonzero = nonZeroCheck(that,rhs); if (javaChecks && nonzero != null) addAssert(that,Label.UNDEFINED_DIV0,treeutils.makeImplies(that.pos, condition, nonzero)); } else if (equality && maxJmlType == jmltypes.TYPE) { rhs = convertExpr(rhs); if (rac) lhs = treeutils.makeUtilsMethodCall(that.pos,"isEqualTo",lhs,rhs); else lhs = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, lhs, rhs); if (tag == JCTree.Tag.NE) lhs = treeutils.makeNot(that.pos, lhs); result = eresult = lhs; // Exit because we are replacing the == operator with a // function call eresult.pos = that.getStartPosition(); treeutils.copyEndPosition(eresult, that); return; } else if (equality && maxJmlType == jmltypes.BIGINT) { lhs = addImplicitConversion(lhs,maxJmlType,lhs); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,maxJmlType,rhs); if (rac) { lhs = treeutils.makeUtilsMethodCall(that.pos,"bigint_eq",lhs,rhs); } else { lhs = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, lhs, rhs); } if (tag == JCTree.Tag.NE) lhs = treeutils.makeNot(that.pos, lhs); result = eresult = lhs; eresult.pos = that.getStartPosition(); treeutils.copyEndPosition(eresult, that); return; } else if (equality && maxJmlType.toString().equals("org.jmlspecs.utils.IJMLTYPE")) { lhs = addImplicitConversion(lhs,maxJmlType,lhs); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,maxJmlType,rhs); if (rac) { eresult = treeutils.makeUtilsMethodCall(that.pos,"equalTYPE",lhs,rhs); } else { eresult = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, lhs, rhs); } if (tag == JCTree.Tag.NE) eresult = treeutils.makeNot(that.pos, eresult); result = eresult; eresult.pos = that.getStartPosition(); treeutils.copyEndPosition(eresult, that); return; } else if (equality && maxJmlType == jmltypes.REAL) { lhs = addImplicitConversion(lhs,maxJmlType,lhs); rhs = convertExpr(rhs); rhs = addImplicitConversion(rhs,maxJmlType,rhs); if (rac) lhs = treeutils.makeUtilsMethodCall(that.pos,"real_eq",lhs,rhs); else lhs = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, lhs, rhs); if (tag == JCTree.Tag.NE) lhs = treeutils.makeNot(that.pos, lhs); result = eresult = lhs; eresult.pos = that.getStartPosition(); treeutils.copyEndPosition(eresult, that); return; // } else if (equality && !lhsIsPrim && !rhsIsPrim) { // } else { Type t = that.type; if (t.getTag() == TypeTag.BOOLEAN) { // Compute a max type - FIXME - need to do this for all types if (that.lhs.type.getTag() == TypeTag.BOT) { t = boxedType(that.rhs.type); } else if (that.rhs.type.getTag() == TypeTag.BOT) { t = boxedType(that.lhs.type); } else if (jmltypes.isJmlType(maxJmlType) || jmltypes.isJmlRepType(maxJmlType)) { t = maxJmlType; } else if (equality && !that.lhs.type.isPrimitive() && !that.rhs.type.isPrimitive()) { t = null; } else { Type tlhs = unboxedType(that.getLeftOperand().type); Type trhs = unboxedType(that.getRightOperand().type); t = tlhs; if (tlhs.getTag() == TypeTag.BOT || (tlhs.getTag().ordinal() < trhs.getTag().ordinal() && trhs.getTag() != TypeTag.BOT)) t = trhs; } } // FIXME - this is incorrect, but handles Jml primitive types at least // if (jmltypes.isJmlType(t)){ if (equality && t == null) {} // OK else if (comp) lhs = addImplicitConversion(lhs,t,lhs); // FIXME - what final type else if (shift) lhs = addImplicitConversion(lhs,unboxedType(that.type),lhs); // FIXME - what final type else lhs = addImplicitConversion(lhs,that.type,lhs); rhs = convertExpr(rhs); if (equality && t == null) {} // OK else if (comp) rhs = addImplicitConversion(rhs,t,rhs); // FIXME - what final type else if (shift) { Type tt = unboxedType(that.rhs.type); if (!tt.equals(syms.longType)) tt = syms.intType; rhs = addImplicitConversion(rhs,syms.intType,rhs); // FIXME - what is tt used for? } else rhs = addImplicitConversion(rhs,that.type,rhs); } // FIXME - add checks for numeric overflow - PLUS MINUS MUL if (!translatingJML) addBinaryChecks(that,tag,lhs,rhs,null); result = eresult = makeBin(that,tag,that.getOperator(),lhs,rhs,maxJmlType); if (!translatingJML) result = eresult = newTemp(eresult); // FIXME - use splitExpressions? } else { Symbol s = that.getOperator(); Type maxJmlType = that.getLeftOperand().type; boolean lhsIsPrim = that.getLeftOperand().type.isPrimitive() && that.getLeftOperand().type.getTag() != TypeTag.BOT; boolean rhsIsPrim = that.getRightOperand().type.isPrimitive() && that.getRightOperand().type.getTag() != TypeTag.BOT; if (jmltypes.isJmlType(that.getRightOperand().type)) maxJmlType = that.getRightOperand().type; if (lhsIsPrim && rhsIsPrim && that.getLeftOperand().type.getTag().ordinal() < that.getRightOperand().type.getTag().ordinal()) maxJmlType = that.getRightOperand().type; Type t = that.type; if (t.getTag() == TypeTag.BOOLEAN) { // Compute a max type - FIXME - need to do this for all types if (jmltypes.isJmlType(maxJmlType)) { t = maxJmlType; } else if (that.lhs.type.getTag() == TypeTag.BOT) { t = boxedType(that.rhs.type); } else if (that.rhs.type.getTag() == TypeTag.BOT) { t = boxedType(that.lhs.type); } else { Type tlhs = unboxedType(that.getLeftOperand().type); Type trhs = unboxedType(that.getRightOperand().type); t = tlhs; if (tlhs.getTag() == TypeTag.BOT || (tlhs.getTag().ordinal() < trhs.getTag().ordinal() && trhs.getTag() != TypeTag.BOT)) t = trhs; } } JCExpression lhs = convertExpr(that.getLeftOperand()); { if (comp) lhs = addImplicitConversion(lhs,t,lhs); else if (shift) lhs = addImplicitConversion(lhs,unboxedType(that.type),lhs); // FIXME - what final type else lhs = addImplicitConversion(lhs,that.type,lhs); } JCExpression rhs = convertExpr(that.getRightOperand()); { if (comp) rhs = addImplicitConversion(rhs,t,rhs); else if (shift) { Type tt = unboxedType(that.rhs.type); if (!tt.equals(syms.longType)) tt = syms.intType; rhs = addImplicitConversion(rhs,tt,rhs); // FIXME - tt or int as above? } else rhs = addImplicitConversion(rhs,that.type,rhs); } addBinaryChecks(that,tag,lhs,rhs,null); JCBinary bin = treeutils.makeBinary(that.pos,tag,that.getOperator(),lhs,rhs); result = eresult = newTemp(bin); } eresult.pos = that.getStartPosition(); // Need to make the start of the temporary Ident the same as the start of the expression it represents treeutils.copyEndPosition(eresult, that); } /** Returns the AST for (rhs != 0), for the appropriate type */ public @Nullable JCExpression nonZeroCheck(JCTree that, JCExpression rhs) { JCExpression nonzero; if (rhs instanceof JCLiteral) { Object value = ((JCLiteral)rhs).value; if (value instanceof Integer && ((Integer)value).intValue() != 0) return null; } else if (rhs instanceof JCTypeCast && ((JCTypeCast)rhs).expr instanceof JCLiteral) { Object value = ((JCLiteral)((JCTypeCast)rhs).expr).value; if (value instanceof Integer && ((Integer)value).intValue() != 0) return null; } if (rac && rhs.type == jmltypes.BIGINT) { nonzero = treeutils.makeUtilsMethodCall(that.pos, "bigint_nonzero", rhs); } else if (rac && rhs.type == jmltypes.REAL) { nonzero = treeutils.makeUtilsMethodCall(that.pos, "real_nonzero", rhs); } else { nonzero = treeutils.makeBinary(that.pos, JCTree.Tag.NE, rhs, treeutils.makeZeroEquivalentLit(rhs.pos, rhs.type)); } return nonzero; } public JCExpression makeBin(JCTree that, JCTree.Tag tag, Symbol opSym, JCExpression lhs, JCExpression rhs, Type maxJmlType) { if (rac && jmltypes.isJmlType(maxJmlType)) { boolean bi = maxJmlType == jmltypes.BIGINT; if (bi || maxJmlType == jmltypes.REAL) { String fcn; switch (tag) { case PLUS: fcn = "add"; break; case MINUS: fcn = "sub"; break; case MUL: fcn = "mul"; break; case DIV: fcn = "div"; break; case MOD: fcn = "mod"; break; case LT: fcn = "lt"; break; case LE: fcn = "le"; break; case GT: fcn = "gt"; break; case GE: fcn = "ge"; break; // FIXME - need shift types default: { String msg = "Unexpected operation tag in JmlAssertionAdder.makeBin: " + tag + " " + JmlPretty.write(that); log.error(that.pos,"jml.internal",msg); throw new JmlInternalError(msg); } } String pre = bi ? "bigint_" : "real_"; return treeutils.makeUtilsMethodCall(that.pos,pre+fcn,lhs,rhs); } else { String msg = "Unexpected JML type in JmlAssertionAdder.makeBin: " + maxJmlType; log.error(that.pos,"jml.internal",msg); throw new JmlInternalError(msg); } } else { return treeutils.makeBinary(that.pos,tag,opSym,lhs,rhs); } } public long maxValue(int pos, TypeTag tag) { switch (tag) { case INT: return Integer.MAX_VALUE; case SHORT: return Short.MAX_VALUE; case LONG: return Long.MAX_VALUE; case BYTE: return Byte.MAX_VALUE; case CHAR: return Character.MAX_VALUE; default: log.error(pos,"jml.internal","Unknown type tag " + tag); return 0; } } public long minValue(int pos, TypeTag tag) { switch (tag) { case INT: return Integer.MIN_VALUE; case SHORT: return Short.MIN_VALUE; case LONG: return Long.MIN_VALUE; case BYTE: return Byte.MIN_VALUE; case CHAR: return Character.MIN_VALUE; default: log.error(pos,"jml.internal","Unknown type tag " + tag); return 0; } } public int comparePrecision(TypeTag a, TypeTag b) { switch (a) { case INT: switch (b) { case INT: return 0; case BYTE: case CHAR: case SHORT: return 1; case FLOAT: case DOUBLE: case LONG: return -1; } break; case BYTE: switch (b) { case BYTE: return 0; case INT: case CHAR: case SHORT: case FLOAT: case DOUBLE: case LONG: return -1; } break; case SHORT: switch (b) { case SHORT: return 0; case BYTE: case CHAR: return 1; case INT: case FLOAT: case DOUBLE: case LONG: return -1; } break; case LONG: switch (b) { case LONG: return 0; case BYTE: case CHAR: case INT: case SHORT: return 1; case FLOAT: case DOUBLE: return -1; } break; } return 0; } @Override public void visitTypeCast(JCTypeCast that) { // Note - casting a null produces a null value Type origType = that.getExpression().type; JCExpression arg = convertExpr(that.getExpression()); JCTree newTypeTree = that.getType(); // the tree, not the Type JCTree clazz = convert(newTypeTree); if (rac && that.type.isPrimitive() && jmltypes.isJmlType(origType)) { if (jmltypes.isSameType(that.type,origType)) { result = eresult = arg; // No-op // FIXME - will need a cast from real to bigint } else { // FIXME - the null here should be an error String s = (origType == jmltypes.BIGINT ? "bigint_to" : origType == jmltypes.REAL ? "real_to" : null) + newTypeTree.toString(); JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); result = eresult = e; } return; } if (rac && that.type.isPrimitive() && jmltypes.isJmlType(that.type) && origType.tsym == jmltypes.repSym((JmlType)that.type)) { result = eresult = arg; // No-op since the representation type is the same as the argument type return; } JCTypeCast castexpr = M.at(that).TypeCast(clazz,arg); castexpr.setType(that.type); // may be superfluous treeutils.copyEndPosition(castexpr,that); JCExpression newexpr = castexpr; if (pureCopy) { result = eresult = newexpr; return; } // Check that expression is null JCExpression eqnull = treeutils.makeEqObject(that.pos,arg,treeutils.makeNullLiteral(that.pos)); JCExpression emax = null, emin = null; int changePrecision = comparePrecision(origType.getTag(), that.type.getTag()); if (types.isSameType(clazz.type,origType)) { // redundant - remove the cast in both rac and esc newexpr = arg; } else if (clazz.type.isPrimitive()) { if (origType.isPrimitive()) { // Java primitive to Java primitive - must be a numeric cast if (changePrecision == 1) { // change precision == 1 means that a higher precision value is being cast to a lower precision // so we check the range of the argument switch (origType.getTag()) { case LONG: emax = treeutils.makeBinary(that.pos, JCTree.Tag.LE, arg, treeutils.makeLit(that.pos, arg.type, Long.valueOf(maxValue(that.pos,that.type.getTag())))); emin = treeutils.makeBinary(that.pos, JCTree.Tag.LE, treeutils.makeLit(that.pos, arg.type, Long.valueOf(minValue(that.pos,that.type.getTag()))), arg); break; case INT: case SHORT: case CHAR: case BYTE: emax = treeutils.makeBinary(that.pos, JCTree.Tag.LE, arg, treeutils.makeLit(that.pos, arg.type, Integer.valueOf((int)maxValue(that.pos,that.type.getTag())))); emin = treeutils.makeBinary(that.pos, JCTree.Tag.LE, treeutils.makeLit(that.pos, arg.type, Integer.valueOf((int)minValue(that.pos,that.type.getTag()))), arg); break; default: log.error(that, "jml.internal", "Unimplemented case combination"); case FLOAT: // FIXME - ignore for now case DOUBLE: emax = emin = treeutils.trueLit; // Must be numeric to numeric - do numeric range checks // FIXME - implement } // reducing precision - make assertions about range of input value addAssert(that, Label.ARITHMETIC_CAST_RANGE, emax); addAssert(that, Label.ARITHMETIC_CAST_RANGE, emin); } else if (changePrecision == -1) { // In this branch we are casting from a lower precision to a higher precision // So we can assume that the range of the value in the new type is the smaller range appropriate to the original type // FIXME the type of the valueOf needs to match the output type switch (origType.getTag()) { case LONG: case INT: case SHORT: case CHAR: case BYTE: emax = treeutils.makeBinary(that.pos, JCTree.Tag.LE, newexpr, treeutils.makeLit(that.pos, arg.type, Integer.valueOf((int)maxValue(that.pos,origType.getTag())))); // FIXME - here and below, INT but we need LONGr emin = treeutils.makeBinary(that.pos, JCTree.Tag.LE, treeutils.makeLit(that.pos, arg.type, Integer.valueOf((int)minValue(that.pos,origType.getTag()))), newexpr); break; default: log.error(that, "jml.internal", "Unimplemented case combination"); case FLOAT: // FIXME - ignore for now case DOUBLE: emax = emin = treeutils.trueLit; // Must be numeric to numeric - do numeric range checks // FIXME - implement } // increasing precision - make assumptions about range of output value addAssume(that, Label.ARITHMETIC_CAST_RANGE, emax); addAssume(that, Label.ARITHMETIC_CAST_RANGE, emin); } } else if (jmltypes.isSameType(origType,jmltypes.BIGINT)) { // \bigint to primitive // FIXME - reducing precision - check range String s = "bigint_to" + that.type.toString(); JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; } else if (jmltypes.isSameType(origType,jmltypes.REAL)) { // \real to primitive // FIXME - reducing precision - check range String s = "real_to" + that.type.toString(); JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; } else { // unboxing = FIXME = check type combinations JCExpression e = treeutils.makeNot(that.pos, eqnull); if (translatingJML) { addAssert(that, Label.UNDEFINED_NULL_UNBOX, treeutils.makeImplies(that.pos, condition, e)); } else { addAssert(that, Label.POSSIBLY_NULL_UNBOX, e); } newexpr = rac ? castexpr : createUnboxingExpr(arg); } } else if (jmltypes.isSameType(clazz.type,jmltypes.BIGINT)) { if (origType.isPrimitive()) { // FIXME - need assumptions about range of output // Java primitive to JML \bigint - must be a numeric cast switch (origType.getTag()) { case LONG: case INT: case SHORT: case CHAR: case BYTE: String s = "bigint_valueOf"; JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; break; case FLOAT: case DOUBLE: // FIXME float/double to bigint // continue using cast expression break; default: log.error(that.pos,"jml.internal","Unexpected cast from " + origType + " to " + that.type); // continue using cast expression } } else if (jmltypes.isSameType(origType,jmltypes.BIGINT)) { // this case should not happen newexpr = arg; } else if (jmltypes.isSameType(origType,jmltypes.REAL)) { // FIXME - reducing precision \real to \bigint String s = "real_to" + that.type.toString(); JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; } else { // FIXME - unboxing numeric reference to \bigint } } else if (jmltypes.isSameType(clazz.type,jmltypes.REAL)) { if (origType.isPrimitive()) { // Java primitive to JML \real - must be a numeric cast // FIXME - need assumptions about range of output String s = "real_valueOf"; JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; } else if (jmltypes.isSameType(origType,jmltypes.BIGINT)) { // convert \bigint to \real String s = "bigint_to" + that.type.toString(); JCExpression e = treeutils.makeUtilsMethodCall(that.pos,s,arg); newexpr = e; } else if (jmltypes.isSameType(origType,jmltypes.REAL)) { // this case should not happen newexpr = arg; } else { // FIXME - unboxing numeric reference to \real } } else { if (origType.isPrimitive()) { // Must be primitive to object (boxing) // OK for Rac newexpr = rac ? castexpr : createBoxingStatsAndExpr(arg,clazz.type); } else if (jmltypes.isSameType(origType,jmltypes.BIGINT)) { // FIXME - box a \bigint to reference } else if (jmltypes.isSameType(origType,jmltypes.REAL)) { // FIXME - box a \real to reference } else { // object to object // For RAC, we check that the expression will not throw an exception // and then we calculate it // For ESC, we do the same check, but we don't do the cast JCTree erasure = clazz; if (clazz instanceof JCTypeApply) erasure = ((JCTypeApply)clazz).clazz; JCExpression typeok = M.at(that).TypeTest(arg, erasure); typeok.setType(syms.booleanType); // FIXME - end position? JCExpression cond = treeutils.makeOr(that.pos, eqnull, typeok); if (javaChecks && localVariables.isEmpty()) { if (!splitExpressions) cond = treeutils.makeImplies(that.pos,condition,cond); if (rac) { addAssert(that,translatingJML ? Label.UNDEFINED_BADCAST : Label.POSSIBLY_BADCAST, cond, arg.type,clazz.type); } else { addAssert(that,translatingJML ? Label.UNDEFINED_BADCAST : Label.POSSIBLY_BADCAST, cond, "a " + arg.type + " cannot be proved to be a " + clazz.type); } } newexpr = castexpr; } } result = eresult = !splitExpressions ? newexpr : newTemp(newexpr); } // OK /** This translates the instanceof operation */ @Override public void visitTypeTest(JCInstanceOf that) { JCExpression lhs = convertExpr(that.getExpression()); JCTree type = that.getType(); JCTree clazz = treeutils.makeType(type.pos, type.type); // No checks needed - Java allows (null instanceof type) // The value is always false JCInstanceOf e = M.at(that).TypeTest(lhs,clazz); e.setType(that.type); treeutils.copyEndPosition(e,that); result = eresult = (translatingJML || pureCopy) ? e : newTemp(e); } // OK @Override public void visitIndexed(JCArrayAccess that) { JCExpression indexed = convertExpr(that.indexed); // FIXME - resolve the use of splitExpressions vs. !pureCopy // FIXME - for now we don't check undefinedness inside quantified expressions if (javaChecks && !pureCopy && localVariables.isEmpty()) { JCExpression nonnull = treeutils.makeNeqObject(that.indexed.pos, indexed, treeutils.makeNullLiteral(that.indexed.getEndPosition(log.currentSource().getEndPosTable()))); if (translatingJML) { nonnull = treeutils.makeImplies(that.pos, condition, nonnull); addAssert(that,Label.UNDEFINED_NULL_DEREFERENCE,nonnull); } else { addAssert(that,Label.POSSIBLY_NULL_DEREFERENCE,nonnull); } } // FIXME _ readable? // checkAccess(JmlTokenKind.ACCESSIBLE, that.indexed, that.indexed, indexed, currentThisId, currentThisId); JCExpression index = convertExpr(that.index); index = addImplicitConversion(index,syms.intType,index); if (javaChecks && !pureCopy && localVariables.isEmpty()) { JCExpression compare = treeutils.makeBinary(that.index.pos, JCTree.Tag.LE, treeutils.intleSymbol, treeutils.makeIntLiteral(that.index.pos, 0), index); if (translatingJML) { compare = treeutils.makeImplies(that.pos, condition, compare); addAssert(that,Label.UNDEFINED_NEGATIVEINDEX,compare); } else { addAssert(that,Label.POSSIBLY_NEGATIVEINDEX,compare); } } JCExpression length = treeutils.makeLength(that.indexed,indexed); if (javaChecks && !pureCopy && localVariables.isEmpty()) { JCExpression compare = treeutils.makeBinary(that.pos, JCTree.Tag.GT, treeutils.intgtSymbol, length, index); // We use GT to preserve the textual order of the subexpressions if (translatingJML) { compare = treeutils.makeImplies(that.pos, condition, compare); addAssert(that,Label.UNDEFINED_TOOLARGEINDEX,compare); } else { addAssert(that,Label.POSSIBLY_TOOLARGEINDEX,compare); } } if (pureCopy && !(that instanceof JmlBBArrayAccess)) { JCArrayAccess aa = M.at(that).Indexed(indexed,index); aa.setType(that.type); result = eresult = aa; } else { JmlBBArrayAccess aa = new JmlBBArrayAccess(null,indexed,index); // FIXME - switch to factory // is the null correct? aa.pos = that.pos; aa.setType(that.type); aa.arraysId = that instanceof JmlBBArrayAccess ? ((JmlBBArrayAccess)that).arraysId : null; JCExpression save = (translatingJML || pureCopy || convertingAssignable) ? aa : newTemp(aa); // assume \typeof(eresult) <: \elemtype(\typeof(indexed)); if (esc && !that.type.isPrimitive() && localVariables.isEmpty()) { // FIXME - we perhaps need quantified sstatements if we are within a qunatified scope JCExpression e1 = treeutils.makeTypeof(save); JCExpression e2 = treeutils.makeElemtype(treeutils.makeTypeof(indexed)); e1 = treeutils.makeSubtype(e1,e2); addAssume(that,Label.IMPLICIT_ASSUME,e1); } if (!pureCopy && !translatingJML) checkAccess(JmlTokenKind.ACCESSIBLE, that, that, aa, currentThisExpr, currentThisExpr); result = eresult = save; } treeutils.copyEndPosition(result, that); } // OK @Override public void visitSelect(JCFieldAccess that) { JCExpression selected; Symbol s = convertSymbol(that.sym); JCExpression trexpr = that.getExpression(); if (!(s instanceof Symbol.TypeSymbol)) trexpr = convertExpr(trexpr); if (pureCopy) { if (s != null) { result = eresult = treeutils.makeSelect(that.pos, trexpr, s); } else { // s can be null if we are visiting a package name result = eresult = treeutils.makeSelect(that.pos, trexpr, that.name); } treeutils.copyEndPosition(result, that); return; } checkRW(JmlTokenKind.READABLE,that.sym,trexpr,that); if (!convertingAssignable) checkAccess(JmlTokenKind.ACCESSIBLE, that, that, that, currentThisExpr, currentThisExpr); if (localVariables.containsKey(s)) { result = eresult = treeutils.makeSelect(that.pos, trexpr, s); } else if (esc && s != null && "class".equals(s.toString())) { result = eresult = treeutils.makeJavaTypelc(that.selected); } else if (translatingJML && s == null) { // This can happen while scanning a store-ref x.* JCExpression sel = trexpr; // FIXME - what if static; what if not a variable JCFieldAccess fa = M.Select(sel, (Name)null); fa.pos = that.pos; fa.sym = null; result = eresult = fa; } else if (translatingJML && s instanceof VarSymbol && attr.isModel(s) && !convertingAssignable && !reps.contains(s)) { JCExpression sel = trexpr; // FIXME - what if static; what if not a variable addRepresentsAxioms((ClassSymbol)that.selected.type.tsym, s, that, trexpr); // if (rac) { // Name mmName = names.fromString(Strings.modelFieldMethodPrefix + that.name.toString()); // JmlSpecs.TypeSpecs tyspecs = specs.getSpecs((ClassSymbol)that.sym.owner); // for (JmlTypeClauseDecl x: tyspecs.modelFieldMethods) { // if (x.decl instanceof JmlMethodDecl && ((JmlMethodDecl)x.decl).name.equals(mmName)) { // JmlMethodDecl md = (JmlMethodDecl)x.decl; // JCMethodInvocation app = treeutils.makeMethodInvocation(that,sel,md.sym); // result = eresult = app; // treeutils.copyEndPosition(result, that); // return; // } // } // if (s.name.toString().equals("erasure") && s instanceof MethodSymbol && utils.qualifiedName((MethodSymbol)s).equals(Strings.jmlSpecsPackage + ".JML.erasure")) { // // JCMethodInvocation m = treeutils.makeUtilsMethodCall(that.pos,"erasure"); // result = eresult = m.meth; // return; // } // // This is an internal error - the wrapper for the model field method // // should have been created during the MemberEnter phase. // throw new NoModelMethod(); // } // if (esc) { // JCFieldAccess fa = M.Select(sel, that.name); // fa.pos = that.pos; // fa.sym = s; // fa.type = that.type; // result = eresult = fa; // if (fa.sym instanceof VarSymbol && specs.isNonNull(fa.sym)) { // JCExpression nonnull = treeutils.makeNeqObject(that.pos,fa,treeutils.nullLit); // condition = treeutils.makeAnd(fa.pos, condition, nonnull); // } // // Use the specs of the (static) type of the object being selected // TypeSpecs tspecs = specs.getSpecs((ClassSymbol)fa.selected.type.tsym); // for (JmlTypeClause tc : tspecs.clauses) { // if (tc.token == JmlToken.REPRESENTS) { // JmlTypeClauseRepresents rep = (JmlTypeClauseRepresents)tc; // // if (((JCIdent)rep.ident).sym.equals(s)) { // // FIXME - why this? //// if (oldenv == null) { //// JmlStatementHavoc st = M.at(that.pos).JmlHavocStatement(List.<JCExpression>of(rep.ident)); //// addStat(st); //// } // if (rep.suchThat){ // addAssume(that,Label.IMPLICIT_ASSUME, // convertExpr(rep.expression)); // // } else { // // FIXME - This does not work if the model variable is used within a pure method spec, because it ends up in the body of a constructed quantifier, but localVariables is not used //// if (localVariables.isEmpty()) { //// addAssumeEqual(that,Label.IMPLICIT_ASSUME, //// convertExpr(rep.ident), //// convertExpr(rep.expression)); //// } else { // JCIdent thisId = currentThisId; // JCExpression thisExpr = currentThisExpr; // try { // currentThisExpr = sel; // currentThisId = sel instanceof JCIdent ? (JCIdent)sel : null; // result = eresult = convertExpr(rep.expression); // } finally { // currentThisId = thisId; // currentThisExpr = thisExpr; // } // // FIXME - we could get recursion here // return; //// } // } // } // } // } return; // } } else if (s instanceof Symbol.TypeSymbol) { // This is a type name, so the tree should be copied, but without inserting temporary assignments // makeType creates a fully-qualified name result = eresult = treeutils.makeType(that.pos,that.type); } else if (translatingJML && esc && !localVariables.isEmpty()) { selected = trexpr; JCFieldAccess fa = treeutils.makeSelect(that.pos,selected,s); result = eresult = fa; if (oldenv != null) { result = eresult = treeutils.makeOld(that.pos,eresult,oldenv); } // } else if (translatingJML) { // selected = convertExpr(that.selected); // boolean var = false; // JCFieldAccess fa = treeutils.makeSelect(that.pos,selected,that.sym); // result = eresult = fa; // if (oldenv != null) { // result = eresult = treeutils.makeOld(that.pos,eresult,oldenv); // } } else if (s instanceof MethodSymbol && s.name.toString().equals("erasure") && utils.qualifiedName((MethodSymbol)s).equals(Strings.jmlSpecsPackage + ".JML.erasure")) { JCMethodInvocation m = treeutils.makeUtilsMethodCall(that.pos,"erasure"); result = eresult = m.meth; } else { selected = trexpr; boolean var = false; if (!utils.isJMLStatic(s) && that.selected instanceof JCIdent && !localVariables.containsKey(((JCIdent)that.selected).sym)) { if (convertingAssignable && currentFresh != null && selected instanceof JCIdent && ((JCIdent)selected).sym == currentFresh.sym) { // continue } else { JCExpression nonnull = treeutils.makeNotNull(that.pos, selected); if (translatingJML) nonnull = treeutils.makeImplies(that.pos, condition, nonnull); if (javaChecks && localVariables.isEmpty()) { addAssert(that, translatingJML? Label.UNDEFINED_NULL_DEREFERENCE : Label.POSSIBLY_NULL_DEREFERENCE, nonnull); // FIXME - what if the dereference happens in a spec in a different file - ,that,log.currentSourceFile()); } } var = true; } if (s.owner instanceof ClassSymbol) { if (specs.isNonNull(s,classDecl.sym) && s instanceof VarSymbol && !localVariables.containsKey(s) && (!methodDecl.sym.isConstructor() || utils.isJMLStatic(that.sym))) { if (convertingAssignable && currentFresh != null && selected instanceof JCIdent && ((JCIdent)selected).sym == currentFresh.sym) { // continue } else { JCFieldAccess e = M.at(that).Select(selected,that.name); e.sym = s; e.type = that.type; JCExpression nl = treeutils.makeNullLiteral(that.pos); treeutils.copyEndPosition(nl,e); JCExpression nonnull = treeutils.makeNeqObject(that.pos, e, nl); addAssume(nonnull,Label.NULL_FIELD,nonnull); } } } // if (!translatingJML && !pureCopy && that.sym != syms.lengthVar) checkAccess(JmlToken.ACCESSIBLE, that, that, (VarSymbol)currentThisId.sym, (VarSymbol)currentThisId.sym); JCFieldAccess fa = treeutils.makeSelect(that.pos,selected,s); fa.type = that.type; // in rac the type can be changed to a representation type result = eresult = (translatingJML || !var || convertingAssignable) ? fa : newTemp(fa); if (oldenv != null) { result = eresult = treeutils.makeOld(that.pos,eresult,oldenv); } } treeutils.copyEndPosition(result, that); if (translatingJML && !pureCopy && s instanceof VarSymbol && specs.isNonNull(s) ) { JCExpression nn = treeutils.makeNeqObject(that.pos,eresult,treeutils.nullLit); condition = treeutils.makeAnd(that.pos, condition, nn); } } public static class NoModelMethod extends RuntimeException { private static final long serialVersionUID = 1L; public NoModelMethod() { super(); } public NoModelMethod(String msg) { super(msg); } } // FIXME - check use of condition everywhere java.util.List<Symbol> reps = new LinkedList<Symbol>(); // OK @Override public void visitIdent(JCIdent that) { if (pureCopy) { JCIdent id = treeutils.makeIdent(that.pos, that.name, that.sym); id.type = that.type; // in case that.sym is null result = eresult = id; treeutils.copyEndPosition(eresult, that); return; } Symbol sym = convertSymbol(that.sym); if (!translatingLHS) checkRW(JmlTokenKind.READABLE,that.sym,currentThisExpr,that); try { // If we are translating the postcondition then parameters // must be mapped to the values at the beginning of the method, // not the current values if (translatingJML) { JCVariableDecl decl; if (!isPostcondition) { JCExpression actual = paramActuals == null ? null : paramActuals.get(sym); if (actual != null) { // Replicate the AST so we are not sharing ASTs across multiple // instances of the original ID. result = eresult = convertCopy(actual); eresult.pos = that.pos; // FIXME - this might be better if the actual were converted to a temporary Ident treeutils.copyEndPosition(eresult, that); return; } } if (isPostcondition && preparams != null && (decl=preparams.get(sym)) != null) { result = eresult = treeutils.makeIdent(that.pos,decl.sym); treeutils.copyEndPosition(eresult, that); return; } } // Check if the id is a model field, and if so: // if rac, then map it to a call of // the corrresponding model method. We have to use model methods // instead of just inlining the represents clause expression because // the model field/method may be overridden in derived classes. if (attr.isModel(sym) && sym instanceof VarSymbol && !convertingAssignable && !reps.contains(sym)) { addRepresentsAxioms(currentThisExpr.type.tsym, sym, that, currentThisExpr); // if (checkAccessEnabled) checkAccess(JmlTokenKind.ACCESSIBLE, that, that, (VarSymbol)currentThisId.sym, (VarSymbol)currentThisId.sym); // FIXME - should we check accessibility for model fields return; } // FIXME - are we expecting fully-qualified arguments? if (checkAccessEnabled) checkAccess(JmlTokenKind.ACCESSIBLE, that, that, that, currentThisId, currentThisId); // Lookup if there is some other translation of the id. For example, // this could be a translation of the formal from the specification // in a parent class mapped to the formal in the target class. It // can also be the mapping from a formal to actual argument. boolean local = false; JCExpression actual = paramActuals == null ? null : paramActuals.get(sym); if (actual != null) { // Replicate the AST so we are not sharing ASTs across multiple // instances of the original ID. result = eresult = convertCopy(actual); eresult.pos = that.pos; treeutils.copyEndPosition(eresult, that); // } else if (sym.type instanceof Type.TypeVar) { // Type t = typevarMapping.get(sym.type.tsym); // result = eresult = treeutils.makeType(that.pos,t); } else if (localVariables.containsKey(sym)) { // just copy - bound in a quantification Symbol sy = localVariables.get(sym); result = eresult = treeutils.makeIdent(that.pos,sy); local = true; } else if (!(sym.owner instanceof Symbol.TypeSymbol)) { // local variable - just leave it as // an ident (the owner is null or the method) JCIdent id = treeutils.makeIdent(that.pos, sym); result = eresult = id; local = true; } else if (currentThisExpr instanceof JCIdent && sym == ((JCIdent)currentThisExpr).sym) { // 'this' - leave it as it is result = eresult = convertCopy(currentThisExpr); } else if (that.name.toString().equals("this")) { result = eresult = currentThisExpr != null ? convertCopy(currentThisExpr) : convertCopy(that); } else if (that.name.toString().equals("super")) { result = eresult = currentThisExpr != null ? convertCopy(currentThisExpr) : convertCopy(that); // FIXME - want this instead of super } else if (that.name.equals(heapVarName)) { result = eresult = convertCopy(that); } else if (sym instanceof Symbol.TypeSymbol) { Type t = that.type; if (t instanceof Type.TypeVar) { t = typevarMapping.get(sym.type.tsym); if (t == null) t = that.type; } // The input id is a type, so we expand it to a FQ name // If the input id is a type variable (that.type instanceof Type.TypeVar) // then makeType creates a new JCIdent, as is appropriate. result = eresult = treeutils.makeType(that.pos, t); } else if (!utils.isJMLStatic(sym)) { // It is a non-static class field, so we prepend the receiver JCExpression cp = convertCopy(currentThisExpr); if (cp.toString().equals("THIS")) ((JCIdent)cp).pos = that.pos; JCExpression fa = treeutils.makeSelect(that.pos,cp,that.sym); // if (sym instanceof VarSymbol && sym.owner instanceof ClassSymbol && specs.isNonNull(sym, classDecl.sym) && !localVariables.isEmpty()) { // JCExpression e = treeutils.makeNotNull(that.pos, fa); // addAssume(that,Label.NULL_FIELD,e); // } result = eresult = fa; } else { // static class field - add the qualified name JCExpression typetree = treeutils.makeType(that.pos, sym.owner.type); JCFieldAccess fa = treeutils.makeSelect(that.pos,typetree, sym); if (!translatingJML && that.sym instanceof VarSymbol && that.sym.owner instanceof ClassSymbol && specs.isNonNull(sym, (ClassSymbol)sym.owner)) { JCExpression e = treeutils.makeNotNull(that.pos, fa); addAssume(that,Label.NULL_FIELD,e); } result = eresult = fa; } treeutils.copyEndPosition(eresult, that); if (oldenv != null && !local) { // FIXME - the old may imappropriately encapsulate the receiver result = eresult = treeutils.makeOld(that.pos, eresult, oldenv); treeutils.copyEndPosition(eresult, that); } } finally { // Note that since 'this' is a ClassSymbol, not a VarSymbol, no check for non-null is made here, which is just fine. if (translatingJML && sym instanceof VarSymbol && specs.isNonNull(sym) && eresult != null) { JCExpression nn = treeutils.makeNotNull(that.pos,convertCopy(eresult)); condition = treeutils.makeAndSimp(that.pos, condition, nn); } } } // OK @Override public void visitLiteral(JCLiteral that) { result = eresult = that; if (fullTranslation) { result = eresult = treeutils.makeDuplicateLiteral(that.pos, that); treeutils.copyEndPosition(eresult, that); } if (pureCopy || rac) return; if (types.isSameType(that.type,syms.stringType)) { JCIdent id = newTemp(that); JCExpression e = treeutils.makeNeqObject(that.pos,id,treeutils.nullLit); addAssume(that,Label.IMPLICIT_ASSUME,e); // These assumptions are necessary so that String literals are // known to satisfy the invariants about Strings addInvariants(id,id.type,id,currentStatements,false,false,false,false,false,true,Label.INVARIANT_ENTRANCE,utils.qualifiedMethodSig(methodDecl.sym)); result = eresult = id; } } // OK @Override public void visitTypeIdent(JCPrimitiveTypeTree that) { // This is a simple primitive type eresult = that; if (fullTranslation) { eresult = M.at(that).TypeIdent(that.typetag).setType(that.type); } result = eresult; } @Override public void visitTypeArray(JCArrayTypeTree that) { // This is a type literal that is an array - we just copy it if (fullTranslation) { result = eresult = M.at(that).TypeArray(convertExpr(that.elemtype)).setType(that.type); } else { result = eresult = that; } } // OK @Override public void visitTypeApply(JCTypeApply that) { // This is a type literal which is the application of a generic type to its parameters result = eresult = !fullTranslation ? that : M.at(that).TypeApply(convertExpr(that.clazz),convertExprList(that.arguments)).setType(that.type); } // OK @Override public void visitTypeParameter(JCTypeParameter that) { // This is a formal class type parameter, possibly including bounds result = !fullTranslation ? that : M.at(that).TypeParameter(that.name, convertExprList(that.bounds)).setType(that.type); } // OK @Override public void visitWildcard(JCWildcard that) { // This is a wildcard (?) type result = eresult = !fullTranslation ? that : M.at(that).Wildcard(convert(that.kind),convert(that.inner)).setType(that.type); } // OK @Override public void visitTypeBoundKind(TypeBoundKind that) { result = !fullTranslation ? that : M.at(that).TypeBoundKind(that.kind).setType(that.type); } // OK @Override public void visitAnnotation(JCAnnotation that) { // Only needed for a full tree copy // A JCAnnotation is a kind of JCExpression if (fullTranslation) { JCTree aType = convertCopy(that.annotationType); List<JCExpression> args = convertCopy(that.args); JCAnnotation a = M.at(that).Annotation(aType,args); a.setType(that.type); treeutils.copyEndPosition(a,that); result = eresult = a; } else { result = eresult = that; } } // TODO: No checks are performed on annotations or modifiers // OK @Override public void visitModifiers(JCModifiers that) { if (fullTranslation) { ListBuffer<JCAnnotation> annotations = new ListBuffer<JCAnnotation>(); for (JCAnnotation a: that.annotations) { annotations.add(convertCopy(a)); } JCModifiers mods = M.at(that).Modifiers(that.flags, annotations.toList()); result = mods; } else { result = that; } } // OK @Override public void visitErroneous(JCErroneous that) { List<? extends JCTree> errs = fullTranslation ? convertCopy(that.errs) : that.errs; result = eresult = M.at(that).Erroneous(errs).setType(that.type); } // OK @Override public void visitLetExpr(LetExpr that) { if (splitExpressions) { JCIdent id = newTempNull(that,that.type); // Adds a declaration pushBlock(); try { for (JCVariableDecl d : that.defs) { convert(d); } JCExpression e = convertExpr((JCExpression)that.expr); addStat(treeutils.makeAssignStat(that.pos,treeutils.makeIdent(id.pos, id.sym),e)); } finally { addStat(popBlock(0L,that)); result = eresult = treeutils.makeIdent(id.pos,id.sym); } } else { result = eresult = M.at(that).LetExpr(convert(that.defs), convert(that.expr)).setType(that.type); } } // OK @Override public void visitJmlBinary(JmlBinary that) { JCExpression saved = condition; try { JCExpression lhs = convertExpr(that.lhs); if (pureCopy) { JCExpression rhs = convertExpr(that.rhs); JCExpression t = M.at(that.pos).JmlBinary(that.op,lhs,rhs); t.type = that.type; result = eresult = t; return; } if (that.op != JmlTokenKind.SUBTYPE_OF && that.op != JmlTokenKind.JSUBTYPE_OF) { lhs = addImplicitConversion(that.lhs,that.type,lhs); } JCExpression rhs,t; switch (that.op) { case IMPLIES: // P ==> Q is !P || Q if (translatingJML) condition = treeutils.makeAnd(that.pos, condition, lhs); if (lhs instanceof JCLiteral && ((JCLiteral)lhs).getValue().equals(Boolean.FALSE)) { eresult = treeutils.makeBooleanLiteral(lhs.pos, true); } else if (splitExpressions) { // temp = true; if (P) { temp = Q; } JCIdent id = newTemp(treeutils.trueLit); pushBlock(); try { rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); addStat(treeutils.makeAssignStat(that.rhs.pos,treeutils.makeIdent(rhs.pos, id.sym), rhs)); } finally { // Particularly NoModelMethod JCBlock bl = popBlock(0L,that.rhs); addStat(M.If(lhs, bl, null)); } eresult = treeutils.makeIdent(that.pos, id.sym); } else { rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); t = treeutils.makeNot(lhs.pos, lhs); t = treeutils.makeOr(that.pos, t, rhs); eresult = t; } if (translatingJML) adjustWellDefinedConditions(lhs); break; case REVERSE_IMPLIES: // P <== Q is P || !Q t = treeutils.makeNot(lhs.pos, lhs); if (translatingJML) condition = treeutils.makeAnd(that.pos, condition, t); if (lhs instanceof JCLiteral && ((JCLiteral)lhs).getValue().equals(Boolean.TRUE)) { eresult = treeutils.makeBooleanLiteral(lhs.pos, true); } else if (splitExpressions) { // temp = true; if (not P) { temp = (not Q); } JCIdent id = newTemp(treeutils.trueLit); pushBlock(); try { rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); addStat(treeutils.makeAssignStat(that.rhs.pos,treeutils.makeIdent(rhs.pos, id.sym), treeutils.makeNot(rhs.pos, rhs))); } finally { JCBlock bl = popBlock(0L,that.rhs); addStat(M.If(treeutils.makeNot(lhs.pos, lhs), bl, null)); } eresult = treeutils.makeIdent(that.pos, id.sym); } else { rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); rhs = treeutils.makeNot(that.rhs.pos, rhs); t = treeutils.makeOr(that.pos, lhs, rhs); eresult = t; } if (translatingJML) adjustWellDefinedConditions(t); break; case EQUIVALENCE: rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); t = treeutils.makeBinary(that.pos, JCTree.Tag.EQ, treeutils.booleqSymbol, lhs, rhs); eresult = splitExpressions ? newTemp(t) : t; break; case INEQUIVALENCE: rhs = convertExpr(that.rhs); rhs = addImplicitConversion(that.rhs,that.type,rhs); t = treeutils.makeBinary(that.pos, JCTree.Tag.NE, treeutils.boolneSymbol, lhs, rhs); eresult = splitExpressions ? newTemp(t) : t; break; case SUBTYPE_OF: // JML subtype rhs = convertExpr(that.rhs); // \TYPE <: \TYPE if (rac) { JCExpression c = methodCallUtilsExpression(that,"isSubTypeOf",lhs,rhs); eresult = splitExpressions ? newTemp(c) : c; } else { JmlMethodInvocation c = treeutils.makeJmlMethodInvocation(that,JmlTokenKind.SUBTYPE_OF,that.type,lhs,rhs); eresult = splitExpressions ? newTemp(c) : c; } break; case JSUBTYPE_OF: // Java subtype rhs = convertExpr(that.rhs); // Class <: Class - in case type checking allows it // TODO - move to a utility method // FIXME - do we intend that <: is always false among pairs of primitive types (even the same) if (rac) { Name n = names.fromString("isAssignableFrom"); Scope.Entry e = rhs.type.tsym.members().lookup(n); Symbol ms = e.sym; JCFieldAccess m = M.at(that).Select(rhs,n); m.sym = ms; m.type = m.sym.type; JCExpression c = M.at(that).Apply(null,m,List.<JCExpression>of(lhs)); c.setType(syms.booleanType); eresult = splitExpressions ? newTemp(c) : c; } else { JmlMethodInvocation c = treeutils.makeJmlMethodInvocation(that,JmlTokenKind.JSUBTYPE_OF,that.type,lhs,rhs); eresult = splitExpressions ? newTemp(c) : c; } break; case LOCK_LT: case LOCK_LE: notImplemented(that,"Lock ordering operations"); throw new JmlNotImplementedException(that,"Lock ordering operations"); // FIXME - need <# <#= operators default: error(that,"Unexpected token in JmlAssertionAdder.visitJmlBinary: " + that.op); } } finally { condition = saved; } result = eresult; eresult.pos = that.getStartPosition(); treeutils.copyEndPosition(eresult, that); } // OK // FIXME - needs work @Override public void visitJmlChoose(JmlChoose that) { result = M.at(that).JmlChoose(that.token,convert(that.orBlocks),convert(that.elseBlock)).setType(that.type); } // FIXME - review this public JCIdent getThisId(Symbol sym) { JCIdent id = thisIds.get(sym); if (id == null) { id = makeThisId(Position.NOPOS,sym); // FIXME - do we really want NOPOS? } return id; } // FIXME - review this public JCIdent makeThisId(int pos, Symbol sym) { VarSymbol THISSym = treeutils.makeVarSymbol(Flags.STATIC,names.fromString(Strings.thisName),sym.type, Position.NOPOS); THISSym.owner = esc ? null : sym; // In esc, the owner is null (instead of sym) to indicate // that this new symbol is a synthetic variable that will not ever // be assigned to. JCIdent id = treeutils.makeIdent(pos,THISSym); this.thisIds.put(sym, id); exprBiMap.put(id,id); return id; } // OK @Override public void visitJmlClassDecl(JmlClassDecl that) { //if (that.name.toString().equals("A")) Utils.stop(); Main.instance(context).pushOptions(that.mods); JmlMethodDecl savedMethodDecl = this.methodDecl; JmlClassDecl savedClassDecl = this.classDecl; ListBuffer<JCTree> savedClassDefs = this.classDefs; ListBuffer<JCStatement> savedCurrentStatements = this.currentStatements; Symbol savedAllocSym = this.allocSym; Symbol savedIsAllocSym = this.isAllocSym; JCIdent savedThisId = this.currentThisId; JCExpression savedThisExpr = this.currentThisExpr; IArithmeticMode savedMode = this.currentArithmeticMode; if (that.sym.isInterface()) { // FIXME - should actually do a pure copy.? result = that; classBiMap.put(that,that); return; } try { this.classDecl = that; this.methodDecl = null; this.currentArithmeticMode = Arithmetic.Math.instance(context).defaultArithmeticMode(classDecl.sym,false); if (esc) { this.currentThisId = makeThisId(classDecl.pos,classDecl.sym); this.currentThisExpr = currentThisId; } else { // rac this.currentThisId = treeutils.makeIdent(classDecl.pos,that.thisSymbol); this.currentThisExpr = currentThisId; } this.classDefs = new ListBuffer<JCTree>(); this.currentStatements = null; if (esc) { JCVariableDecl d = treeutils.makeStaticVarDef(syms.intType,heapVarName,classDecl.sym, treeutils.makeIntLiteral(0, 0)); heapSym = d.sym; //initialStatements.add(d); this.classDefs.add(d); } { JCVariableDecl d = treeutils.makeVarDef(syms.intType,names.fromString(Strings.allocName),classDecl.sym, treeutils.makeNullLiteral(classDecl.pos)); allocSym = d.sym; d = treeutils.makeVarDef(syms.booleanType,names.fromString(Strings.isAllocName),classDecl.sym, treeutils.makeNullLiteral(classDecl.pos)); isAllocSym = d.sym; } enclosingClass = that.sym; if (rac) { // For RAC, we need to be sure there are no missing model field methods // because of a represents clause representing a super class field. JmlSpecs.TypeSpecs tyspecs = that.typeSpecs; if (tyspecs != null) { for (JmlTypeClause t: tyspecs.clauses) { switch (t.token){ case REPRESENTS: boolean pv = checkAccessEnabled; checkAccessEnabled = false; // Do not check access in JML clauses try { scan(t); } finally { checkAccessEnabled = pv; } break; default: // skip } } } } for (JCTree t: that.defs) { scan(t); } // FIXME - need to fixup RAC and ESC check o fstatic initialization if (!pureCopy) { JCBlock bl = checkStaticInitialization(); if (bl != null) this.classDefs.add(bl); } JmlSpecs.TypeSpecs tyspecs = that.typeSpecs; if (!rac) if (tyspecs != null) { for (JmlTypeClause t: tyspecs.clauses) { switch (t.token){ case REPRESENTS: boolean pv = checkAccessEnabled; checkAccessEnabled = false; // Do not check access in JML clauses try { scan(t); } finally { checkAccessEnabled = pv; } break; case INVARIANT: case CONSTRAINT: case INITIALLY: case AXIOM: case IN: case MAPS: case READABLE: case WRITABLE: // skip break; default: log.error(t.pos,"jml.internal","Clause type not handled in visitJmlClassDecl: " + t.token.internedName()); } } // for (JmlTypeClause t: tyspecs.decls) { // switch (t.token){ // case JMLDECL: // scan(t); // break; // default: // log.error(t.pos,"jml.internal","Clause type not handled in visitJmlClassDecl: " + t.token.internedName()); // } // } // if (rac) for (JmlClassDecl t: tyspecs.modelTypes) { // scan(t); // } } // JmlSpecs.TypeSpecs typeSpecs = specs.getSpecs(classDecl.sym); //// for (JmlTypeClauseDecl m: typeSpecs.modelFieldMethods) { //// this.classDefs.add(m); //// } List<JCTree> defs = this.classDefs.toList(); for (JCTree def: defs) { if (def instanceof JmlMethodDecl) { JmlMethodDecl jdef = (JmlMethodDecl)def; String nm = jdef.name.toString(); if (attr.isModel(jdef.sym) && nm.startsWith(Strings.modelFieldMethodPrefix)) { if ((jdef.mods.flags & Flags.DEFAULT) != 0) { // We are presuming that all represents clauses are processed // (as part of scanning the specs defs in visitJmlClassDecl) // before we handle all the model field methods. utils.warning(jdef.source(),jdef.pos,"jml.no.model.method.implementation",nm.substring(Strings.modelFieldMethodPrefix.length())); // FIXME - extract name of model field } } } } // FIXME - replicate all the other AST nodes List<JCTypeParameter> typarams = that.typarams; if (fullTranslation) typarams = convert(typarams); JmlClassDecl n = M.at(that).ClassDef(convert(that.mods),that.name,typarams,convertExpr(that.extending),convertExprList(that.implementing),defs); n.sym = that.sym; n.setType(that.type); n.superSymbol = that.superSymbol; n.thisSymbol = that.thisSymbol; n.toplevel = that.toplevel; // FIXME - translate to new top level n.docComment = that.docComment; n.env = that.env; // FIXME - translate? n.specsDecl = that.specsDecl; // FIXME - these may be self-references - and I think there are now only one n.typeSpecs = null; //that.typeSpecs; // not copied - FIXME? here and elsewhere if (savedClassDefs != null && n.sym.owner instanceof ClassSymbol) { savedClassDefs.add(n); } else if (currentStatements != null) { addStat(n); } result = n; classBiMap.put(that,n); } finally { this.classDecl = savedClassDecl; this.methodDecl = savedMethodDecl; this.classDefs = savedClassDefs; this.currentThisId = savedThisId; this.currentThisExpr = savedThisExpr; this.currentStatements = savedCurrentStatements; this.allocSym = savedAllocSym; this.isAllocSym = savedIsAllocSym; this.currentArithmeticMode = savedMode; Main.instance(context).popOptions(); } } // FIXME - review protected JCBlock checkStaticInitialization() { JmlMethodDecl md = methodSymForInitBlock(classDecl, Flags.STATIC, classDecl); pushBlock(); for (Symbol s: classDecl.sym.getEnclosedElements()) { if (utils.isJMLStatic(s) && !s.type.isPrimitive() && s instanceof VarSymbol) { VarSymbol v = (VarSymbol)s; if (specs.isNonNull(v)) { JCIdent id = treeutils.makeIdent(v.pos, v); JCExpression e = treeutils.makeNeqObject(v.pos, id, treeutils.nullLit); //e = convertJML(e); JCStatement st = addAssert(new JCDiagnostic.SimpleDiagnosticPosition(v.pos),Label.STATIC_INIT,e,"non-null static field has null value: " + v.name); if (st instanceof JCAssert) { e = ((JCAssert)st).cond; } else if (st instanceof JCIf) { e = ((JCIf)st).cond; if (e instanceof JCUnary) e = ((JCUnary)e).arg; } else { e = null; } if (e instanceof JCIdent) { ((JCIdent)e).sym.owner = md.sym; } } } } // FIXME - what are the rules about accessibility for static initializers boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { addInvariants(classDecl,classDecl.sym.type,null,currentStatements,true,false,false,false,true,false,Label.STATIC_INIT, "invariant is false"); } finally { checkAccessEnabled = pv; } JCBlock bl = popBlock(Flags.STATIC,classDecl); if (onlyComments(bl.stats)) bl = null; return bl; } // OK @Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlCompilationUnit while translating JML: " + that.getClass()); return; } List<JCTree> defs = convert(that.defs); JmlCompilationUnit n = M.at(that).TopLevel(that.packageAnnotations,that.pid,defs); n.docComments = that.docComments; // FIXME: need to translate map n.endPositions = that.endPositions; // FIXME: need to convert map? // n.flags = that.flags; n.mode = that.mode; n.lineMap = that.lineMap; n.namedImportScope = that.namedImportScope; n.packge = that.packge; n.setType(that.type); // n.parsedTopLevelModelTypes = that.parsedTopLevelModelTypes; // FIXME: need to convert n.sourcefile = that.sourcefile; n.starImportScope = that.starImportScope; n.specsCompilationUnit = that.specsCompilationUnit; // FIXME: need to convert // n.specsTopLevelModelTypes = convert(that.specsTopLevelModelTypes); result = n; } //OK @Override public void visitJmlMethodSig(JmlMethodSig that) { result = M.at(that).JmlConstraintMethodSig(convertExpr(that.expression),convertExprList(that.argtypes)); result.setType(that.type); } // OK @Override public void visitJmlDoWhileLoop(JmlDoWhileLoop that) { if (pureCopy) { JCExpression e = convertExpr(that.cond); JmlDoWhileLoop loop = M.at(that).DoLoop(null,e); try { treeMap.put(that, loop); JCStatement bl = convert(that.body); loop.body = bl; loop.setType(that.type); loop.loopSpecs = convert(that.loopSpecs); result = loop; } finally { treeMap.remove(that); } addStat(loop); return; } /* * loop_invariant INV * loop_variant VAR * label: do { BODY } while (COND); * becomes * { * ... declare index * assert INV * label: do { * havoc * assume INV * TEMP = VAR * assert 0 <= TEMP * loop_bodyN: { * ... statements from BODY * ... continue --> break loop_bodyN; * ... break --> break; * } * ... statements from COND // put before INV for side-effects * assert INV * TEMP2 = VAR * assert TEMP2 < TEMP * if (!COND') { * assert INV; * break; * } * } while (true); * } * */ // FIXME - need label for loop if any and the target mapping // Outer block to restrict scopes of temporaries pushBlock(); JCVariableDecl indexDecl = loopHelperDeclareIndex(that); java.util.List<JCIdent> decreasesIDs = new java.util.LinkedList<JCIdent>(); // Create this early so it is available as a target JmlWhileLoop loop = M.at(that).WhileLoop(treeutils.trueLit,null); treeMap.put(that, loop); // Test that invariants hold before entering loop loopHelperInitialInvariant(that.loopSpecs); // New loop body pushBlock(); // Havoc all items that might be changed in the loop if (esc) { loopHelperHavoc(that.body,indexDecl,null,that.body,that.cond); } loopHelperAssumeInvariants(that.loopSpecs, decreasesIDs, that); // Now in the loop, so check that the variants are non-negative loopHelperCheckNegative(decreasesIDs, that); // Then translate the original loop body loopHelperMakeBody(that.body); // Now compute any side-effects of the loop condition addTraceableComment(that.cond,that.cond,"Loop test"); JCExpression cond = convertExpr(that.cond); // increment the index loopHelperIncrementIndex(indexDecl); // In a do-while loop we test the condition before the invariants // Construct the loop test and the exit block. loopHelperMakeBreak(that.loopSpecs,cond,loop,that); // After the loop, check the invariants and check that the variants have decreased loopHelperAssertInvariants(that.loopSpecs,decreasesIDs); // Finish up the new loop body // Finish up the output block loopHelperFinish(loop,that); } // OK @Override public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) { if (pureCopy) { JCExpression expr = convertExpr(that.expr); JCVariableDecl var = convert(that.var); // FIXME - should not do this two-level design JCEnhancedForLoop jloop = M.at(that).ForeachLoop(var,expr,null); List<JmlStatementLoop> loopSpecs = that.loopSpecs == null ? null : convertCopy(that.loopSpecs); JmlEnhancedForLoop loop = M.at(that).JmlEnhancedForLoop(jloop, loopSpecs); jloop.type = loop.type = that.type; try { treeMap.put(that, jloop); JCStatement bl = convertIntoBlock(that.body,that.body); loop.body = bl; loop.loopSpecs = that.loopSpecs == null ? null : convertCopy(that.loopSpecs); result = loop; } finally { treeMap.remove(that); } // FIXME - map the sym of the loop variable here, and in other loop types, just as below? addStat(loop); return; } /* loop_invariant INV * loop_variant VAR * label: for (T v: ARRAY) BODY * becomes * { * ... statements from ARRAY * ... declare INDEX = 0 * ... compute LENGTH * assert INV * label: while (true) { * havoc * assume INV * TEMP = VAR * if (INDEX >= LENGTH) { * assert INV; * break; * } * assert 0 <= TEMP * declare v = ARRAY[INDEX] * loop_bodyN: { * ... statements from BODY * ... continue --> break loop_bodyN; * ... break --> break; * } * INDEX = INDEX + 1 * assert INV * TEMP2 = VAR * assert TEMP2 < TEMP * } * // FIXME - if break exits still have to satisfy invariant, put the check here * } * */ /* loop_invariant INV * loop_variant VAR * label: for (T v: ITERABLE) BODY * becomes * { * ... statements from ITERABLE * ... declare INDEX = 0 * ... declare ITERATOR = ITERABLE.iterator() * assert INV * label: while (true) { * havoc * assume INV * TEMP = VAR * if (ITERATOR.hasNext()) { * assert INV; * break; * } * declare v = ITERATOR.next() * assert 0 <= TEMP * loop_bodyN: { * ... statements from BODY * ... continue --> break loop_bodyN; * ... break --> break; * } * INDEX = INDEX + 1 * assert INV * TEMP2 = VAR * assert TEMP2 < TEMP * } * // FIXME - if break exits still have to satisfy invariant, put the check here * } * */ // FIXME - need label for loop if any and the target mapping // Outer block to restrict scopes of temporaries pushBlock(); JCExpression array = convertExpr(that.expr); JCVariableDecl indexDecl = loopHelperDeclareIndex(that);; java.util.List<JCIdent> decreasesIDs = new java.util.LinkedList<JCIdent>(); // Create this early so it is available as a target JmlWhileLoop loop = M.at(that).WhileLoop(treeutils.trueLit,null); treeMap.put(that, loop); if (that.expr.type.getTag() == TypeTag.ARRAY) { JCExpression lengthExpr = treeutils.makeLength(array, array); lengthExpr = newTemp(lengthExpr); // FIXME - could give it a recognizable name // Test that invariants hold before entering loop loopHelperInitialInvariant(that.loopSpecs); // New loop body pushBlock(); // Havoc all items that might be changed in the loop if (esc) { loopHelperHavoc(that.body,indexDecl,null,that.body,that.expr); } // Assume the invariants // Compute and remember the variants loopHelperAssumeInvariants(that.loopSpecs,decreasesIDs,that); // Compute the condition, recording any side-effects { JmlSingleton index = M.at(that.pos).JmlSingleton(JmlTokenKind.BSINDEX); index.type = syms.intType; JCExpression ocond = treeutils.makeBinary(that.pos, JCTree.Tag.LT, index, lengthExpr); JCExpression cond = convertJML(ocond); addTraceableComment(ocond,ocond,"Loop test"); // The exit block tests the condition; if exiting, it tests the // invariant and breaks. loopHelperMakeBreak(that.loopSpecs,cond,loop,that); } // Now in the loop, so check that the variants are non-negative loopHelperCheckNegative(decreasesIDs, that); JCArrayAccess aa = new JmlBBArrayAccess(null,array,treeutils.makeIdent(that.pos, indexDecl.sym)); aa.setType(that.var.type); JCVariableDecl loopVarDecl = treeutils.makeVarDef(that.var.type, that.var.name, methodDecl.sym, aa); loopVarDecl.sym = that.var.sym; // We share syms; if we don't we have to add // a mapping to paramActuals addStat(loopVarDecl); } else { // Have an iterable instead of an array Name iteratorName = names.fromString("_JML_iterator_" + (++count)); JCExpression iter = methodCallUtilsExpression(array,"iterator",array); Type restype; restype = new Type.ClassType.ClassType(Type.noType,List.<Type>of(array.type.getTypeArguments().head),iter.type.tsym); JCVariableDecl decl = treeutils.makeVarDef( restype, iteratorName, methodDecl.sym, iter); addStat(decl); // Test that invariants hold before entering loop loopHelperInitialInvariant(that.loopSpecs); // New loop body pushBlock(); // Havoc all items that might be changed in the loop if (esc) { loopHelperHavoc(that.body,indexDecl,null,that.expr,that.body); } // Assume the invariants // Compute and remember the variants loopHelperAssumeInvariants(that.loopSpecs,decreasesIDs,that); // Compute the condition, recording any side-effects { // iterator.hasNext() JCExpression ocond = methodCallUtilsExpression(iter,"hasNext", treeutils.makeIdent(array.pos, decl.sym)); JCExpression cond = convertCopy(ocond); addTraceableComment(ocond,ocond,"Loop test"); // The exit block tests the condition; if exiting, it tests the // invariant and breaks. loopHelperMakeBreak(that.loopSpecs,cond,loop,that); } // Now in the loop, so check that the variants are non-negative loopHelperCheckNegative(decreasesIDs, that); // T v = ITERATOR.next() JCExpression value = methodCallUtilsExpression(iter,"next", treeutils.makeIdent(array.pos, decl.sym)); value.type = that.iterDecl.type.getTypeArguments().get(0); value = newTemp(value); value = addImplicitConversion(array, that.var.type, value); JCVariableDecl vd = ( treeutils.makeVarDef( that.var.type, that.var.name, methodDecl.sym, value) ); vd.sym = that.var.sym; // We share syms; if we don't we have to add // a mapping to paramActuals addStat(vd); } // Then translate the original loop body // Have to do some footwork to get the Block object before constructing its contents loopHelperMakeBody(that.body); // increment the index loopHelperIncrementIndex(indexDecl); // After the loop, check the invariants and check that the variants have decreased loopHelperAssertInvariants(that.loopSpecs,decreasesIDs); // Finish up the new loop body // Finish up the output block loopHelperFinish(loop,that); // Does two popBlock operations // FIXME Need to add specifications; also index and values variables // JCVariableDecl v = M.at(that.var.pos).VarDef(that.var.sym,null); // v.setType(that.var.type); // JCExpression e = scanExpr(that.expr); // pushBlock(); // // Now havoc any variables changed in the loop // { // ListBuffer<JCExpression> targets = TargetFinder.findVars(that.getStatement(),null); // TargetFinder.findVars(that.getExpression(),targets); // // synthesize a modifies list // JmlStatementHavoc st = M.at(that.body.pos).JmlHavocStatement(targets.toList()); // addStat(st); // } // scan(that.body); // JCBlock b = popBlock(0,that.body.pos); // addStat(M.at(that).ForeachLoop(v, e, b)); // result? } /** Declares the synthetic index variable for the loop, adding the declaration * to the current statement list. */ // OK protected JCVariableDecl loopHelperDeclareIndex(DiagnosticPosition pos) { int p = pos.getPreferredPosition(); if (p<0) p = 0; Name indexName = names.fromString("index_" + p + "_" + (++count)); JCVariableDecl indexDecl = treeutils.makeVarDef(syms.intType, indexName, methodDecl.sym, treeutils.zero); indexDecl.sym.pos = pos.getPreferredPosition(); indexDecl.pos = pos.getPreferredPosition(); addStat(indexDecl); indexStack.add(0,indexDecl); return indexDecl; } /** Finds variables assigned in the loop body and adds a havoc statement */ // OK // FIXME - needs checking that we are getting all of needed variables protected void loopHelperHavoc(DiagnosticPosition pos, JCVariableDecl indexDecl, List<? extends JCTree> initlist, List<? extends JCTree> list, JCTree... trees) { ListBuffer<JCExpression> targets = new ListBuffer<JCExpression>(); if (list != null) for (JCTree t: list) { TargetFinder.findVars(t,targets); } for (JCTree t: trees) { TargetFinder.findVars(t,targets); } targets.add(treeutils.makeIdent(pos.getPreferredPosition(),indexStack.get(0).sym)); // synthesize a modifies list ListBuffer<JCExpression> newlist = new ListBuffer<JCExpression>(); for (JCExpression target: targets.toList()) { JCExpression newtarget = null; if (target instanceof JCArrayAccess) { JCArrayAccess aa = (JCArrayAccess)target; if (aa.index instanceof JCIdent) { JCIdent id = (JCIdent)aa.index; if (initlist != null && initlist.length() == 1) { JCTree t = initlist.head; if (t instanceof JCVariableDecl) { JCVariableDecl vd = (JCVariableDecl)t; if (id.sym == vd.sym) { // newtarget = aa; } } } } // FIXME - we convert array accesses to the entire array. // This is partly because the array index may depend on the loop variable, which is also havoced. // But the test for whether the index is in range occurs before the invariants are assumed, // which would put the index back in range. if (newtarget == null) newtarget = M.at(target.pos).JmlStoreRefArrayRange(aa.indexed,null,null).setType(target.type); } else { newtarget = target; } newlist.add(convertJML(newtarget)); } int p = pos.getPreferredPosition(); JmlStatementHavoc st = M.at(p).JmlHavocStatement(newlist.toList()); // FIXME - this ought to work to preserve initialized values at the beginning of th e0th iteration, but makes loops infeasbile // JCExpression id = treeutils.makeIdent(indexDecl.pos, indexDecl.sym); // JCExpression e = treeutils.makeBinary(p, JCTree.Tag.GT, id, treeutils.zero); // addStat(M.at(p).If(e, st, null)); addStat(st); } // FIXME: implement loop_modifies? /** Finds variables assigned in the loop body and adds a havoc statement */ // OK // FIXME - needs checking that we are getting all of needed variables protected void loopHelperHavoc(DiagnosticPosition pos, JCVariableDecl indexDecl, /*@ nullable*/ List<? extends JCTree> initlist, JCTree... trees) { loopHelperHavoc(pos, indexDecl, initlist, null, trees); } /** Adds a statement to increment the index variable */ protected void loopHelperIncrementIndex(JCVariableDecl var) { JCIdent oldid = treeutils.makeIdent(var.pos, var.sym); JCIdent newid = treeutils.makeIdent(var.pos, var.sym); addStat( treeutils.makeAssignStat(var.pos, newid, treeutils.makeBinary(var.pos, JCTree.Tag.PLUS, treeutils.intplusSymbol, oldid, treeutils.one))); } /** Creates initial assert statements for any loop invariants */ protected void loopHelperInitialInvariant(List<JmlStatementLoop> loopSpecs) { if (loopSpecs != null) { for (JmlStatementLoop inv: loopSpecs) { if (inv.token == JmlTokenKind.LOOP_INVARIANT) { try { JCExpression copy = convertCopy(inv.expression); // Might throw NoModelMethod addTraceableComment(inv,copy,inv.toString()); JCExpression e = convertJML(copy); addAssert(inv,Label.LOOP_INVARIANT_PRELOOP, e); } catch (NoModelMethod e) { // continue - skip the assertion } } } } } /** Create the if statement that is the loop exit */ protected void loopHelperMakeBreak(List<JmlStatementLoop> loopSpecs, JCExpression cond, JCTree loop, DiagnosticPosition pos) { pushBlock(); JCBreak br = M.at(pos).Break(null); br.target = loop; addStat(br); JCBlock bl = popBlock(0,pos); JCExpression ncond = treeutils.makeNot(pos.getPreferredPosition(),cond); addStat(M.at(pos).If(ncond,bl,null)); } /** Convert the loop body */ protected void loopHelperMakeBody(JCStatement body) { addTraceableComment(body,null,"Begin loop body"); JCBlock bodyBlock = M.at(body).Block(0, null); Name label = names.fromString("loopBody_" + body.pos + "_" + (++count)); JCLabeledStatement lab = M.at(body).Labelled(label,bodyBlock); continueStack.push(lab); addStat(lab); try { JCBlock bl = convertIntoBlock(body,body); bodyBlock.stats = bl.stats; } finally { continueStack.pop(); } } /** Inserts the invariants as assumptions; also computes initial values of * the variants. (at the beginning of a loop body) */ protected void loopHelperAssumeInvariants(List<JmlStatementLoop> loopSpecs, java.util.List<JCIdent> decreasesIDs, JCTree that) { addTraceableComment(that, null, "Begin loop check"); DiagnosticPosition pos = that; // Assume the invariants if (loopSpecs != null) { for (JmlStatementLoop inv: loopSpecs) { if (inv.token == JmlTokenKind.LOOP_INVARIANT) { try { JCExpression copy = convertCopy(inv.expression); addTraceableComment(inv,copy,inv.toString()); JCExpression e = convertJML(copy); addAssume(inv,Label.LOOP_INVARIANT_ASSUMPTION, e); } catch (NoModelMethod e ) { // continue - no assertions added } } } } { JCVariableDecl indexDecl = indexStack.get(0); JCIdent id = treeutils.makeIdent(pos.getPreferredPosition(),indexDecl.sym); JCBinary bin = treeutils.makeBinary(pos.getPreferredPosition(),JCTree.Tag.LE,treeutils.intleSymbol,treeutils.zero,id); addAssume(pos,Label.IMPLICIT_ASSUME, bin); } // Compute and remember the variants if (loopSpecs != null) { for (JmlStatementLoop inv: loopSpecs) { if (inv.token == JmlTokenKind.DECREASES) { try { JCExpression copy = convertCopy(inv.expression); addTraceableComment(inv,copy,inv.toString(),"Initial Value of Loop Decreases Expression"); JCExpression e = convertJML(copy); JCIdent id = newTemp(e); id.pos = inv.pos; decreasesIDs.add(id); } catch (NoModelMethod e) { // continue } } } } } /** Check that the variants are non-negative */ protected void loopHelperCheckNegative(java.util.List<JCIdent> decreasesIDs, DiagnosticPosition pos) { for (JCIdent id: decreasesIDs) { JCBinary compare = treeutils.makeBinary(pos.getPreferredPosition(), JCTree.Tag.LE, treeutils.intleSymbol, treeutils.zero, id); addAssert(id,Label.LOOP_DECREASES_NEGATIVE,compare); } } /** Asserts the invariants and that the variants are decreasing */ protected void loopHelperAssertInvariants(List<JmlStatementLoop> loopSpecs, java.util.List<JCIdent> decreasesIDs) { if (loopSpecs != null) { for (JmlStatementLoop inv: loopSpecs) { if (inv.token == JmlTokenKind.LOOP_INVARIANT) { try { JCExpression copy = convertCopy(inv.expression); addTraceableComment(inv,copy,inv.toString()); JCExpression e = convertJML(copy); addAssert(inv,Label.LOOP_INVARIANT, e); } catch (NoModelMethod e) { // continue } } } Iterator<JCIdent> iter = decreasesIDs.iterator(); for (JmlStatementLoop inv: loopSpecs) { if (inv.token == JmlTokenKind.DECREASES) { try { JCExpression copy = convertCopy(inv.expression); addTraceableComment(inv,copy,inv.toString()); JCExpression e = convertJML(copy); JCIdent id = newTemp(e); JCBinary bin = treeutils.makeBinary(inv.pos, JCTree.Tag.LT, treeutils.intltSymbol, id, iter.next()); addAssert(inv,Label.LOOP_DECREASES, bin); } catch (NoModelMethod e) { // continue } } } } } /** Completes the loop */ protected void loopHelperFinish(JmlWhileLoop loop, JCTree that) { JCBlock bl = popBlock(0,that); loop.body = bl; loop.setType(that.type); addStat(loop); treeMap.remove(that); indexStack.remove(0); addStat(popBlock(0,that)); } // OK @Override public void visitJmlForLoop(JmlForLoop that) { if (pureCopy) { List<JCStatement> init = convert(that.init); JCExpression cond = convertExpr(that.cond); List<JCExpressionStatement> step = convert(that.step); JmlForLoop loop = M.at(that).ForLoop(init,cond,step,null); try { treeMap.put(that, loop); JCStatement bl = convertIntoBlock(that.body,that.body); loop.body = bl; loop.setType(that.type); loop.loopSpecs = convertCopy(that.loopSpecs); result = loop; } finally { treeMap.remove(that); } addStat(loop); return; } addTraceableComment(that,"for ..."); /* loop_invariant INV * loop_variant VAR * label: for (INIT; COND; STEP) BODY * becomes * { * ... statements from INIT * assert INV * label: while (true) { * havoc * assume INV * TEMP = VAR * ... statements from COND * if (!COND') { * assert INV; * break; * } * assert 0 <= TEMP * loop_bodyN: { * ... statements from BODY * ... continue --> break loop_bodyN; * ... break --> break; * } * ... statements from STEP * assert INV * TEMP2 = VAR * assert TEMP2 < TEMP * } * // FIXME - if break exits still have to satisfy invariant, put the check here * } * */ // FIXME - need label for loop if any and the target mapping // Outer block to restrict scopes of temporaries pushBlock(); if (that.init != null) scan(that.init); JCVariableDecl indexDecl = loopHelperDeclareIndex(that); java.util.List<JCIdent> decreasesIDs = new java.util.LinkedList<JCIdent>(); // Create this early so it is available as a target JmlWhileLoop loop = M.at(that).WhileLoop(treeutils.trueLit,null); treeMap.put(that, loop); // Test that invariants hold before entering loop loopHelperInitialInvariant(that.loopSpecs); // New loop body pushBlock(); // Havoc all items that might be changed in the loop if (esc) { loopHelperHavoc(that.body,indexDecl,that.init,that.step,that.body,that.cond); } loopHelperAssumeInvariants(that.loopSpecs, decreasesIDs, that); // Compute the condition, recording any side-effects if (that.cond != null) { addTraceableComment(that.cond,that.cond,"Loop test"); JCExpression cond = convertExpr(that.cond); // The exit block tests the condition; if exiting, it breaks out of the loop loopHelperMakeBreak(that.loopSpecs, cond, loop, that); } // Now in the loop, so check that the variants are non-negative loopHelperCheckNegative(decreasesIDs, that); // Then translate the original loop body // Have to do some footwork to get the Block object before constructing its contents loopHelperMakeBody(that.body); if (that.step != null) scan(that.step); // increment the index loopHelperIncrementIndex(indexDecl); // After the loop, check the invariants and check that the variants have decreased loopHelperAssertInvariants(that.loopSpecs,decreasesIDs); // Finish up the new loop body // Finish up the output block loopHelperFinish(loop,that); } @Override public void visitJmlGroupName(JmlGroupName that) { JmlGroupName g = M.at(that).JmlGroupName(convertCopy(that.selection)); // FIXME - not sure about the kind of copying g.setType(that.type); g.sym = that.sym; result = g; } // OK @Override public void visitJmlImport(JmlImport that) { // FIXME - need to rewrite qualid - might have a *; might have a method name result = M.at(that).Import(that.qualid, that.staticImport).setType(that.type); } int lblUnique = 0; // OK @Override public void visitJmlLblExpression(JmlLblExpression that) { JCExpression expr = convertExpr(that.expression); if (pureCopy) { result = eresult = M.at(that).JmlLblExpression(that.labelPosition, that.token, that.label, expr).setType(that.type); return; } // The format of this string is important in interpreting it in JmlEsc String nm = Strings.labelVarString + that.token.internedName().substring(1) + "_" + that.label + "_" + (++lblUnique); JCIdent id = newTemp(that.getLabelPosition(),nm,expr); id.pos = that.pos; result = eresult = id; if (esc) ((VarSymbol)id.sym).pos = Position.NOPOS; // TODO: Why? if (rac) { JCExpression lit = treeutils.makeLit(that.getPreferredPosition(),syms.stringType,that.label.toString()); String name = null; Type t = eresult.type; TypeTag tag = t.getTag(); if (!t.isPrimitive()) { name = "reportObject"; } else if (tag == TypeTag.INT) { name = "reportInt"; } else if (tag == TypeTag.BOOLEAN) { name = "reportBoolean"; } else if (tag == TypeTag.LONG) { name = "reportLong"; } else if (tag == TypeTag.CHAR) { name = "reportChar"; } else if (tag == TypeTag.SHORT) { name = "reportShort"; } else if (tag == TypeTag.FLOAT) { name = "reportFloat"; } else if (tag == TypeTag.DOUBLE) { name = "reportDouble"; } else if (tag == TypeTag.BYTE) { name = "reportByte"; } else if (tag == TypeTag.BOT) { name = "reportObject"; } else if (t instanceof JmlType) { name = "reportObject"; } else { // this is a type error - should never get here error(that,"Unknown type in a \\lbl expression: " + t); } if (name != null) { JCFieldAccess m = findUtilsMethod(that,name); JCMethodInvocation c = M.at(that).Apply(null,m,List.<JCExpression>of(lit,treeutils.makeIdent(id.pos,id.sym))); c.type = id.type; JCStatement st = M.at(that).Exec(c); if (that.token == JmlTokenKind.BSLBLPOS) { // Only report if the expression is true // It is a type error if it is not boolean st = M.at(that).If(treeutils.makeIdent(id.pos,id.sym), st, null); } else if (that.token == JmlTokenKind.BSLBLNEG) { // Only report if the expression is false // It is a type error if it is not boolean st = M.at(that).If(treeutils.makeNot(that.pos, treeutils.makeIdent(id.pos,id.sym)), st, null); } addStat(st); } } } // OK @Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable that) { JmlMethodClauseCallable mc = M.at(that).JmlMethodClauseCallable(that.keyword); mc.setType(that.type); mc.methodSignatures = convert(that.methodSignatures); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { JmlMethodClauseConditional mc = M.at(that).JmlMethodClauseConditional( that.token, convertExpr(that.expression), convertExpr(that.predicate)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { JmlMethodClauseDecl mc = M.at(that).JmlMethodClauseDecl( that.token, convert(that.decls)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { JmlMethodClauseExpr mc = M.at(that).JmlMethodClauseExpr( that.token, convertExpr(that.expression)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { JmlMethodClauseGroup mc = M.at(that).JmlMethodClauseGroup( convert(that.cases)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { JmlMethodClauseSignals mc = M.at(that).JmlMethodClauseSignals( that.token, convert(that.vardef), convertExpr(that.expression)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { JmlMethodClauseSignalsOnly mc = M.at(that).JmlMethodClauseSignalsOnly( that.token, convertExprList(that.list)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { JmlMethodClauseStoreRef mc = M.at(that).JmlMethodClauseStoreRef( that.token, convertExprList(that.list)); mc.setType(that.type); mc.sourcefile = that.sourcefile; result = mc; } // OK @Override public void visitJmlMethodDecl(JmlMethodDecl that) { if (that.name.toString().equals("tick")) Utils.stop(); // If we visit this method by visiting its containing class, classDecl will be set // but if we call this visit method directly, e.g., from the api, // it will not be, and we need to find the class if (esc && JmlEsc.skip(that)) return; if (rac && (JmlEsc.skipRac(that) || utils.hasAny(that.mods, Flags.ABSTRACT))) { if (classDefs != null) classDefs.add(that); // FIXME - should make a fresh copy of the declaration return; } String nm = that.name.toString(); // if (attr.isModel(that.sym) && nm.startsWith(Strings.modelFieldMethodPrefix)) { //// if (classDefs != null) classDefs.add(that); // return; // } if (classDecl == null) classDecl = utils.getOwner(that); log.useSource(that.source()); boolean saved = translatingJML; try { // FIXME - implemente constructors - need super calls. // if (that.restype == null) { classDefs.add(that); return; } // FIXME - implement constructors JCBlock body = null; if (pureCopy) { JmlMethodDecl savedMD = methodDecl; methodDecl = that; body = convertIntoBlock(that.body,that.body); methodDecl = savedMD; } else { body = convertMethodBodyNoInit(that,classDecl); } List<JCTypeParameter> typarams = that.typarams; if (fullTranslation) typarams = convertCopy(typarams); // FIXME - is there anything to be translated List<JCVariableDecl> params = that.params; if (fullTranslation) params = convertCopy(params); // Just a copy - the parameters are just modifiers, types, and names JCExpression restype = that.restype; if (fullTranslation) restype = convertExpr(restype); JmlMethodDecl m = M.MethodDef(convert(that.mods), that.name, restype, typarams, null, params, convertExprList(that.thrown), body, convertExpr(that.defaultValue)); m.pos = that.pos; m.sym = that.sym; m.setType(that.type); m._this = that._this; m.sourcefile = that.sourcefile; //m.owner = that.owner; // FIXME - new class decl? m.docComment = that.docComment; m.cases = convertCopy(that.cases); m.methodSpecsCombined = that.methodSpecsCombined; // FIXME - copy? m.specsDecl = that.specsDecl; // FIXME - needs new reference if (classDefs != null) classDefs.add(m); // classDefs can be null if JmlEsc.check is called directly on a JCMethodDecl // FIXME - not working yet // if (rac && that.sym.isStatic() && that.name.toString().equals("main")) { // FIXME - check the arguments? // Name n = names.fromString("_JML_racE"); // ClassSymbol sym = attr.createClass("java.lang.NoClassDefFoundError"); // JCVariableDecl decl = treeutils.makeVarDef(sym.type, n, that.sym, that.pos); // // decl.type = // JCExpression msg = treeutils.makeStringLiteral(that.pos, "Executable is compiled with RAC, but the classpath is missing the jmlruntime.jar"); // JCExpression ty = M.at(that).Type(syms.runtimeExceptionType); // JCExpression newex = M.at(that).NewClass(null, List.<JCExpression>nil(), ty, List.<JCExpression>of(msg), null); // JCThrow th = M.at(that).Throw(newex); // JCBlock bl = M.at(that).Block(0L, List.<JCStatement>of(th)); // JCCatch catcher = M.at(that).Catch(decl,bl); // JCTry tr = M.at(that).Try(m.body, List.<JCCatch>of(catcher), null); // m.body = M.at(that).Block(0L, List.<JCStatement>of(tr)); // } result = m; methodBiMap.put(that,m); } catch (JmlNotImplementedException e) { // FIXME _ if it is actually the synthetic method for a model field we used to use this //notImplemented("represents clause containing ", ee, that.source()); if (that.name.toString().startsWith(Strings.modelFieldMethodPrefix)) { notImplemented("represents clause containing ",e); } else { notImplemented("method (or represents clause) containing ",e); } log.error(e.pos,"jml.unrecoverable", "Unimplemented construct in a method or model method or represents clause"); } finally { translatingJML = saved; } } //// FIXME - Docuiment - should this be used where generic typeargs exist? protected JCExpression translateTypeArg(JCExpression targ) { // Argument is a type, not an expression, so we // replace it with a type literal if (targ instanceof JCTree.JCTypeApply) { JCTree.JCTypeApply ta = (JCTree.JCTypeApply)targ; JCTree.JCFieldAccess f = M.Select(ta.clazz,names._class); f.type = syms.classType; f.sym = f.type.tsym; int size = ta.arguments.size(); ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); for (JCExpression ttarg : ta.arguments) { args.add(translateTypeArg(ttarg)); } Type elemtype = args.first().type; JCNewArray argsarray = M.NewArray(M.Type(elemtype), List.<JCExpression>of(treeutils.makeIntLiteral(Position.NOPOS, size)), args.toList()); argsarray.type = new Type.ArrayType(elemtype,syms.arrayClass); return methodCallUtilsExpression(targ,"makeTYPE",f,argsarray); } else if (targ instanceof JCTree.JCWildcard) { return methodCallUtilsExpression(targ,"makeTYPEQ"); // FIXME - need to handle more subtypes differently, I'm sure } else { // JCPrimitiveTypeTree, JCFieldAccess, JCIdent, JCArrayTypeTree JCTree.JCFieldAccess f = M.Select(targ,names._class); f.type = syms.classType; f.sym = f.type.tsym; return methodCallUtilsExpression(targ,"makeTYPE0",f); } } // OK /** Creates a type-checked expression for (expr).getClass() - for RAC */ protected JCExpression methodCallgetClass(JCExpression expr) { Name n = names.fromString("getClass"); Scope.Entry e = syms.objectType.tsym.members().lookup(n); Symbol ms = e.sym; JCFieldAccess m = M.Select(expr,n); m.sym = ms; m.type = m.sym.type; JCExpression c = M.Apply(null,m,List.<JCExpression>nil()); c.setType(syms.classType); return c; } // FIXME - what about generic types // FIXME - what about arrays of arrays /** Translates \typeof(arg) */ protected void translateTypeOf(JCMethodInvocation tree) { JCExpression arg = tree.args.get(0); TypeTag tag = arg.type.getTag(); switch (tag) { case ARRAY: case CLASS: eresult = methodCallgetClass(convertExpr(arg)); break; case BOOLEAN: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Boolean"); break; case INT: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Integer"); break; case LONG: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Long"); break; case SHORT: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Short"); break; case BYTE: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Byte"); break; case FLOAT: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Float"); break; case DOUBLE: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Double"); break; case CHAR: eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Character"); break; default: log.error(arg.pos,"jml.unknown.construct","typeof for " + arg.type,"JmlRac.translateTypeOf"); // We give it an arbitrary value // FIXME - or do we call it undefined eresult = treeutils.makePrimitiveClassLiteralExpression("java.lang.Boolean"); break; } // Make a \TYPE from a Java class literal result = eresult = methodCallUtilsExpression(tree,"makeTYPE0",eresult); } // TODO: Review /** Translates \type(arg) */ protected JCExpression translateType(JCExpression arg) { if (arg.type instanceof Type.TypeVar) { Type tt = typevarMapping.get(arg.type.tsym); if (tt == null) tt = arg.type; JCExpression t = treeutils.makeType(arg.pos, tt); if (t != null) arg = t; return arg; } boolean pv = checkAccessEnabled; checkAccessEnabled = false; try { return convertExpr(arg); } finally { checkAccessEnabled = pv; } } /** Helper method to translate \elemtype expressions for RAC */ protected void translateElemtype(JCMethodInvocation tree) { // OK for Java types, but not complete for JML types - FIXME JCExpression arg = tree.args.head; arg = convertExpr(arg); if (tree.type == JmlTypes.instance(context).TYPE) { arg = methodCallUtilsExpression(tree,"erasure",arg); } Name n = names.fromString("getComponentType"); Scope.Entry e = syms.classType.tsym.members().lookup(n); Symbol ms = e.sym; JCFieldAccess m = M.Select(arg,n); m.sym = ms; m.type = m.sym.type; JCExpression c = M.Apply(null,m,List.<JCExpression>nil()); c.setType(syms.classType); result = eresult = c; if (tree.type == JmlTypes.instance(context).TYPE) { result = eresult = methodCallUtilsExpression(tree,"makeTYPE0",c); } } protected Utils.DoubleMap<Name,Symbol,JCVariableDecl> oldarrays = new Utils.DoubleMap<Name,Symbol,JCVariableDecl>(); protected void escAddToOldList(JCIdent label, JCStatement stat) { if (label.equals(preLabel)) { pushBlock(); currentStatements = oldStatements; addStat(stat); popBlock(); } else { // FIXME _ I don;'t beklieve the use of currentStsatements is correct here ListBuffer<JCStatement> list = labelActiveOldLists.get(label); if (list != null) { list.add(stat); } else { ListBuffer<JCStatement> stlist = labelOldLists.get(label); ListBuffer<JCStatement> newlist = new ListBuffer<JCStatement>(); for (JCStatement st: stlist) { if (st instanceof JCLabeledStatement && ((JCLabeledStatement)st).label.equals(label)) { newlist.addAll(currentStatements); } newlist.add(st); } stlist.clear(); stlist.addAll(newlist); } } } protected boolean inOldEnv = false; // OK // These are special JML fcn-like calls (e.g. \old, \nonnullelements, ...) @Override public void visitJmlMethodInvocation(JmlMethodInvocation that) { if (pureCopy && !rac) { if (that.token == JmlTokenKind.BSOLD || that.token == JmlTokenKind.BSPAST|| that.token == JmlTokenKind.BSPRE) { boolean savedInOldEnv = inOldEnv; inOldEnv = true; JCExpression arg0 = convertExpr(that.args.get(0)); JmlMethodInvocation m; if (that.args.size() > 1) { JCExpression arg1 = that.args.get(1); arg1 = M.at(arg1).Ident(((JCIdent)arg1).name); m = M.at(that).JmlMethodInvocation(that.token, List.<JCExpression>of(arg0,arg1)); } else { m = M.at(that).JmlMethodInvocation(that.token, List.<JCExpression>of(arg0)); } m.setType(that.type); m.label = that.label; m.startpos = that.startpos; m.varargsElement = that.varargsElement; // typeargs and meth are always null for a JML operation result = eresult = m; inOldEnv = savedInOldEnv; return; } else { JmlMethodInvocation m; if (that.token != null) { m = M.at(that).JmlMethodInvocation(that.token, convertExprList(that.args)); } else { m = M.at(that).JmlMethodInvocation(convertExpr(that.meth), convertExprList(that.args)); } m.setType(that.type); m.label = that.label; m.startpos = that.startpos; m.varargsElement = that.varargsElement; // typeargs and meth are always null for a result = eresult = m; return; } } switch (that.token) { case BSPAST: case BSOLD: case BSPRE: { JCIdent savedEnv = oldenv; // FIXME _ this implementation is not adequate for checking postconditions with \old from callers // FIXME - also it does not work for rac at labelled locations boolean savedInOldEnv = inOldEnv; inOldEnv = true; try { if (rac) { ListBuffer<JCStatement> saved = currentStatements; try { Name label = null; if (that.args.size() == 2) { currentStatements = new ListBuffer<JCStatement>(); label = ((JCIdent)that.args.get(1)).name; } else { currentStatements = oldStatements; label = defaultOldLabel; } JCExpression arg = (that.args.get(0)); if (!convertingAssignable && arg instanceof JCArrayAccess && ( ((JCArrayAccess)arg).indexed instanceof JCIdent || ((JCArrayAccess)arg).indexed instanceof JCFieldAccess)) { JCArrayAccess aa = (JCArrayAccess)arg; Symbol sym = treeutils.getSym(aa.indexed); JCExpression ad = convertExpr(aa.indexed); JCExpression a = treeutils.copyArray(that.pos,ad); JCVariableDecl d = newTempDecl(arg, a.type); d.init = a; oldarrays.put(label, sym, d); JCStatement v = d; // FIXME - combine with duplicate code below addStat(v); // FIXME - what happens to simple \old if (that.args.size() == 2) { ListBuffer<JCStatement> list = labelActiveOldLists.get(label); if (list != null) { list.add(v); } else { ListBuffer<JCStatement> stlist = labelOldLists.get(label); ListBuffer<JCStatement> newlist = new ListBuffer<JCStatement>(); for (JCStatement st: stlist) { if (st instanceof JCLabeledStatement && ((JCLabeledStatement)st).label.equals(label)) { newlist.addAll(currentStatements); } newlist.add(st); } stlist.clear(); stlist.addAll(newlist); } } JCIdent id = treeutils.makeIdent(arg.pos,d.sym); JCArrayAccess newaa = M.at(aa.pos).Indexed(id, aa.index); newaa.type = aa.type; visitIndexed(newaa); // The above call sets result and eresult } else { arg = convertExpr(arg); String s = "_JML___old_" + (++count); // FIXME - Put string in Strings Name nm = names.fromString(s); JCVariableDecl v = treeutils.makeVarDef(arg.type,nm,methodDecl.sym,treeutils.makeZeroEquivalentLit(arg.pos,arg.type)); //v.mods.flags |= Flags.FINAL; // If we use this, the sym has to be set FINAL as well. addStat(v); pushBlock(); addStat(treeutils.makeAssignStat(arg.pos, treeutils.makeIdent(arg.pos, v.sym), arg)); JCBlock bl = popBlock(0,arg); JCTry st = makeRACTry(bl,"_JML__old_ex",arg); addStat(st); if (label != null) { //JCExpression e = that.args.get(1); ListBuffer<JCStatement> list = labelActiveOldLists.get(label); if (list != null) { list.add(v); } else { ListBuffer<JCStatement> stlist = labelOldLists.get(label); if (stlist != currentStatements) { ListBuffer<JCStatement> newlist = new ListBuffer<JCStatement>(); for (JCStatement stt: stlist) { if (stt instanceof JCLabeledStatement && ((JCLabeledStatement)stt).label.equals(label)) { newlist.addAll(currentStatements); } newlist.add(stt); } stlist.clear(); stlist.addAll(newlist); } } } JCIdent id = treeutils.makeIdent(arg.pos,v.sym); eresult = id; } } finally { currentStatements = saved; inOldEnv = savedInOldEnv; } } else { // esc if (that.args.size() == 1) { oldenv = preLabel; // FIXME - could have a constant name for this } else { // The second argument is a label, held as a JCIdent oldenv = (JCIdent)that.args.get(1); } JCExpression m = convertExpr(that.meth); JCExpression arg = convertExpr(that.args.get(0)); // convert is affected by oldenv // We have to wrap this in an old (even though it sometimes wraps twice) // in order to get arrays properly resolved arg = treeutils.makeOld(that.pos, arg, oldenv); // JmlMethodInvocation meth; // if (that.args.size() == 1) { // meth = M.at(that).JmlMethodInvocation(that.token,arg); // } else { // // The second argument is a label, held as a JCIdent // meth = M.JmlMethodInvocation(that.token,arg,that.args.get(1)); // } // meth.setType(that.type); // meth.pos = that.pos; // meth.startpos = that.startpos; // meth.varargsElement = that.varargsElement; // meth.meth = m; // meth.label = that.label; // meth.typeargs = that.typeargs; // FIXME - do these need translating? // eresult = meth; eresult = arg; } } finally { oldenv = savedEnv; inOldEnv = savedInOldEnv; } } break; case BSNONNULLELEMENTS : { int n = that.args.size(); if (n == 0) { result = eresult = treeutils.trueLit; } else { JCExpression conj = null; for (JCExpression arg : that.args) { JCExpression e = convertExpr(arg); e = rac ? methodCallUtilsExpression(arg,"nonnullElementCheck",e) : treeutils.makeAnd(that.pos, treeutils.makeNeqObject(that.pos, e, treeutils.nullLit), treeutils.makeJmlMethodInvocation(arg, that.token,that.type,e)); conj = conj == null? e : treeutils.makeAnd(arg.pos, conj, e); } result = eresult = conj; } } break; case BSTYPEOF: { if (rac) { translateTypeOf(that); } if (esc) { JCExpression arg = convertExpr(that.args.get(0)); JmlMethodInvocation meth = M.at(that).JmlMethodInvocation(that.token,arg); meth.startpos = that.startpos; meth.varargsElement = that.varargsElement; meth.meth = that.meth; meth.type = that.type; meth.label = that.label; meth.typeargs = that.typeargs; // FIXME - do these need translating? result = eresult = meth; } } break; case BSTYPELC: if (rac) { JCExpression arg = that.args.get(0); if (that.javaType) { JCTree.JCFieldAccess f = M.Select(arg,names._class); f.type = syms.classType; f.sym = f.type.tsym; result = eresult = f; } else { result = eresult = translateTypeArg(arg); } // FIXME - used to be something like result = eresult = treeutils.trType(that.pos, type); ??? } if (esc) { JCExpression t = translateType(that.args.get(0)); JmlMethodInvocation e = treeutils.makeJmlMethodInvocation(that, that.token, that.type, t); e.javaType = that.javaType; result = eresult = e; } break; case BSERASURE: { JCExpression arg = that.args.get(0); if (arg.type == syms.classType) { // no-op result = eresult = convertExpr(arg); } else if (rac) { arg = convertJML(arg); result = eresult = treeutils.makeUtilsMethodCall(that.pos,"erasure",arg); } else if (esc) { JCExpression t = translateType(arg); result = eresult = treeutils.makeJmlMethodInvocation(that, that.token, that.type, t); t = treeutils.makeNeqObject(eresult.pos, eresult, treeutils.nullLit); if (splitExpressions) addAssume(t,Label.IMPLICIT_ASSUME,t); } break; } case BSELEMTYPE: if (rac) translateElemtype(that); if (esc) { JCExpression arg = that.args.get(0); if (arg.type == jmltypes.TYPE) { JCExpression t = translateType(that.args.get(0)); result = eresult = treeutils.makeJmlMethodInvocation(that, JmlTokenKind.BSELEMTYPE, that.type, t); } else { result = eresult = treeutils.makeJmlMethodInvocation(that, JmlTokenKind.BSTYPEOF, that.type, convertJML(arg)); result = eresult = treeutils.makeJmlMethodInvocation(that, JmlTokenKind.BSELEMTYPE, that.type, eresult); } } break; case BSFRESH : { if (rac) throw new JmlNotImplementedException(that,that.token.internedName());// FIXME else { int p = that.pos; JCExpression arg = convertJML(that.args.get(0)); if (localVariables.isEmpty()) arg = newTemp(arg); // We make a temp variable so that the (converted) argument is not captured by the \old, except in a quantified expression // FIXME _ I don't think this works properly if no temp is allocated JCFieldAccess fa = isAllocated(that,arg); JCExpression n = treeutils.makeOld(that.pos,fa,preLabel); JCExpression prev = treeutils.makeNot(that.pos, n); fa = isAllocated(that,convertCopy(arg)); JCExpression ee = treeutils.makeAnd(that.pos, prev, fa); JCExpression e = treeutils.makeNeqObject(that.pos, arg, treeutils.nullLit); ee = e; //treeutils.makeAnd(that.pos, e, ee); fa = treeutils.makeSelect(p, convertCopy(arg), allocSym); if (assumingPostConditions) { e = treeutils.makeEquality(p, fa, treeutils.makeIntLiteral(p, ++allocCounter) ); } else { e = treeutils.makeBinary(p, JCTree.Tag.GT, treeutils.intgtSymbol, fa, treeutils.makeIntLiteral(p, 0) ); } result = eresult = treeutils.makeAnd(that.pos, e, ee); } break; } case BSDISTINCT: { // Any type error should have been reported in JmlAttr boolean anyPrimitive = false; Type maxPrimitiveType = null; for (JCExpression arg : that.args) { Type tt = arg.type; if (tt.isErroneous()) continue; if (tt.isPrimitive()) { anyPrimitive = true; } } if (anyPrimitive) for (JCExpression arg : that.args) { Type tt = arg.type; if (arg.type.isErroneous()) continue; if (!tt.isPrimitive()) tt = types.unboxedType(tt); if (arg.type.getTag() == TypeTag.VOID) { // FIXME -error } else if (maxPrimitiveType == null) { maxPrimitiveType = arg.type; } else if (types.isConvertible(tt,maxPrimitiveType)) { // OK } else if (types.isConvertible(maxPrimitiveType, tt)) { maxPrimitiveType = tt; } else { // FIXME - error } } ListBuffer<JCExpression> newargs = new ListBuffer<JCExpression>(); for (JCExpression arg : that.args) { JCExpression ex = convertExpr(arg); if (anyPrimitive) ex = addImplicitConversion(arg,maxPrimitiveType,ex); newargs.add(ex); } result = eresult = M.at(that.pos).JmlMethodInvocation(JmlTokenKind.BSDISTINCT, newargs.toList()); eresult.type = syms.booleanType; break; } case BSINVARIANTFOR : { JCExpression res = null; for (JCExpression arg: that.args) { JCExpression a = treeutils.isATypeTree(arg)? null : convertJML(arg); JCExpression e = getInvariantAll(that,arg.type,a); res = e == null ? res : res == null ? e : treeutils.makeAnd(that, res, e); } if (res == null) res = treeutils.trueLit; result = eresult = res; break; } case BSKEY: { ExpressionExtension ext = (ExpressionExtension)Extensions.instance(context).find(that.pos, that.token, true); eresult = ext.assertion(this, that); if (eresult == null) { Log.instance(context).error("esc.internal.error","Unimplemented token in JmlAssertionAdder (no ExpressionExtension.assertion method): " + that.token.internedName()); result = null; throw new JmlNotImplementedException(that,that.token.internedName()); } break; } case BSMAX : case BSREACH : case BSSPACE : case BSWORKINGSPACE : case BSDURATION : case BSISINITIALIZED : case BSNOWARN: case BSNOWARNOP: case BSWARN: case BSWARNOP: case BSBIGINT_MATH: case BSSAFEMATH: case BSJAVAMATH: // FIXME - not implemented throw new JmlNotImplementedException(that,that.token.internedName()); default: Log.instance(context).error("esc.internal.error","Unknown token in JmlAssertionAdder: " + that.token.internedName()); throw new JmlNotImplementedException(that,that.token.internedName()); } result = eresult; } @Nullable JCExpression getInvariantAll(DiagnosticPosition pos, Type baseType, JCExpression obj) { JCExpression res = null; for (Type ty: parents(baseType,true)) { // FIXME - make sure parents() works for TypeVar JCExpression e = getInvariant(pos, baseType, ty, obj); res = e == null ? res : res == null ? e : treeutils.makeAnd(pos, res, e); } return res; } @Nullable JCExpression getInvariant(@NonNull DiagnosticPosition pos, @NonNull Type base, @NonNull Type t, @Nullable JCExpression obj) { if (!(t.tsym instanceof ClassSymbol)) return null; TypeSpecs tspecs = specs.getSpecs((ClassSymbol)t.tsym); JCExpression saved = currentThisExpr; currentThisExpr = convertJML(obj); JCExpression result = null; try { for (JmlTypeClause clause: tspecs.clauses) { if (clause.token != JmlTokenKind.INVARIANT) continue; if (!utils.visible(base.tsym, t.tsym, clause.modifiers.flags)) continue; if (obj == null && !hasStatic(clause.modifiers)) continue; JCExpression e = convertJML(((JmlTypeClauseExpr)clause).expression); result = result == null ? e : treeutils.makeAnd(pos,result,e); } } finally { currentThisExpr = saved; } return result; } protected JCTry makeRACTry(JCBlock bl, String name, JCExpression arg) { pushBlock(); JCBlock cbl = popBlock(0,arg); JCVariableDecl ex = treeutils.makeVarDef(syms.exceptionType, names.fromString(name), methodDecl.sym, arg.pos); DiagnosticPosition pos = arg; boolean quiet = utils.jmlverbose == 0; JCCatch catcher1; JCVariableDecl vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchMethodError").type, names.fromString("noSuchMethodError"), methodDecl.sym, arg.pos); if (quiet) { catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); location = treeutils.makeNullLiteral(pos.getPreferredPosition()); // FIXME - really need to propagate the location of the call JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchMethod",id,location); catcher1 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } JCCatch catcher2; vd = treeutils.makeVarDef(utils.createClassSymbol("java.lang.NoSuchFieldError").type, names.fromString("noSuchFieldError"), methodDecl.sym, arg.pos); if (quiet) { catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>nil())); } else { JCExpression id = treeutils.makeIdent(pos.getPreferredPosition(),vd.sym); JCExpression location = treeutils.makeStringLiteral(pos.getPreferredPosition(), utils.locationString(pos, log.currentSourceFile())); if (Utils.testingMode) location = treeutils.makeNullLiteral(pos.getPreferredPosition()); JCMethodInvocation m = treeutils.makeUtilsMethodCall(pos.getPreferredPosition(),"reportNoSuchField",id, location); catcher2 = M.at(pos).Catch(vd, M.Block(0L, List.<JCStatement>of(M.at(pos.getPreferredPosition()).Exec(m)))); } JCTry st = M.at(arg.pos).Try(bl,List.<JCCatch>of(M.at(arg.pos).Catch(ex,cbl),catcher1,catcher2),null); // FIXME - don't add try if isOnlyComment(bl) is true return st; } @Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { if (!pureCopy) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlMethodSpecs: " + that.getClass()); return; } List<JmlSpecificationCase> cases = convertCopy(that.cases); JmlMethodSpecs ms = M.at(that).JmlMethodSpecs(cases); ms.setType(that.type); ms.decl = that.decl; // FIXME - copy - needs remapping ms.deSugared = that.deSugared; // FIXME - copy ms.forExampleCases = that.forExampleCases; // FIXME - copy ms.impliesThatCases = that.impliesThatCases; // FIXME - copy result = ms; } // OK @Override public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { result = M.at(that).JmlModelProgramStatement(convert(that.item)).setType(that.type); } // OK @Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) { result = eresult = M.at(that).JmlPrimitiveTypeTree(that.token).setType(that.type); } /** Maps symbols declared in quantified and let statements to new symbols - needed because * quantified symbols can be used in multiple places */ protected java.util.Map<Symbol,Symbol> localVariables = new java.util.HashMap<Symbol,Symbol>(); protected ListBuffer<JCStatement> nonignoredStatements = null; // OK @Override public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) { for (JCVariableDecl d: that.decls) { localVariables.put(d.sym,d.sym); } result = eresult = treeutils.makeZeroEquivalentLit(that.pos,that.type); try { if (!splitExpressions) { ListBuffer<JCStatement> prev = nonignoredStatements; try { if (translatingJML) { nonignoredStatements = currentStatements; pushBlock(); // FIXME - we have not implemented guarding conditions for expressions inside quantifiers } JmlQuantifiedExpr q = M.at(that). JmlQuantifiedExpr(that.op, convertCopy(that.decls), convertExpr(that.range), convertExpr(that.value)); q.setType(that.type); result = eresult = q; } finally { if (translatingJML) { popBlock(); nonignoredStatements = prev; } } } else { java.util.List<Bound> bounds = new java.util.LinkedList<Bound>(); JCExpression innerexpr = determineRacBounds(that.decls,that.range,bounds); if (innerexpr == null) { log.note(that,"rac.not.implemented.quantified"); return; } // The accumulator variable Type t = that.type; Name n = names.fromString("_JML$val$$" + (++count)); JCVariableDecl decl = treeutils.makeVarDef(t, n, methodDecl.sym, that.pos); addStat(decl); decl.init = treeutils.makeZeroEquivalentLit(that.pos, t); // Label for the loop, so we can break out of it Name label = names.fromString("__JMLwhile_" + (++count)); pushBlock(); // enclosing block, except for declaration of accumulator try { for (Bound bound: bounds) { JCVariableDecl indexdef = treeutils.makeVarDef(bound.decl.type,bound.decl.name,methodDecl.sym,bound.decl.pos); localVariables.put(bound.decl.sym, indexdef.sym); JCIdent id = treeutils.makeIdent(that.pos, decl.sym); // accumulator JCIdent idd = treeutils.makeIdent(that.pos, decl.sym); // another id for the accumulator JCStatement st; JCBreak brStat = null; JCBlock bl; pushBlock(); // Start of loop block try { JCExpression guard = convertExpr(innerexpr); pushBlock(); // Start of guarded block try { JCExpression val = convertExpr(that.value); switch (that.op) { case BSFORALL: // if (guard) { val = convert(value); if (!val) { accumulator = false; break <label>; }} decl.init = treeutils.trueLit; pushBlock(); addStat(treeutils.makeAssignStat(that.pos, id, treeutils.falseLit)); addStat(brStat = M.Break(label)); bl = popBlock(0L,that); st = M.If(treeutils.makeNot(that.pos,val), bl, null); break; case BSEXISTS: // if (guard) { val = convert(value); if (!val) { accumulator = true; break <label>; }} pushBlock(); addStat(treeutils.makeAssignStat(that.pos, id, treeutils.trueLit)); addStat(brStat = M.Break(label)); bl = popBlock(0L,that); st = M.If(val, bl, null); break; case BSSUM: // if (guard) { val = convert(value); accumulator = accumulator + val; } st = treeutils.makeAssignStat(that.pos,id,treeutils.makeBinary(that.pos, JCTree.Tag.PLUS, idd, val)); break; case BSPRODUCT: // if (guard) { val = convert(value); accumulator = accumulator * val; } switch (that.type.getTag()) { case INT: decl.init = treeutils.makeIntLiteral(that.pos, Integer.valueOf(1)); break; case LONG: decl.init = treeutils.makeLit(that.pos, that.type, Long.valueOf(1)); break; case SHORT: decl.init = treeutils.makeLit(that.pos, that.type, Short.valueOf((short)1)); break; case BYTE: decl.init = treeutils.makeLit(that.pos, that.type, Byte.valueOf((byte)1)); break; case DOUBLE: decl.init = treeutils.makeLit(that.pos, that.type, Double.valueOf(1)); break; case FLOAT: decl.init = treeutils.makeLit(that.pos, that.type, Float.valueOf(1)); break; // Skipping CHAR - multiplying chars does not make sense. default: log.note(that,"rac.not.implemented.quantified"); throw new JmlNotImplementedException(that,"RAC not implemented for this type: " + that.type); } st = treeutils.makeAssignStat(that.pos,id,treeutils.makeBinary(that.pos, JCTree.Tag.MUL, idd, val)); break; case BSNUMOF: // if (guard) { val = convert(value); if (val) accumulator = accumulator + 1; st = treeutils.makeAssignStat(that.pos,id,treeutils.makeBinary(that.pos, JCTree.Tag.PLUS, idd, idd.type.getTag() == TypeTag.LONG ? treeutils.longone : treeutils.one)); // FIXME - what about bigint? st = M.at(that).If(val, st, null); break; case BSMAX: case BSMIN: // if (guard) { val = convert(value); if ( accumulator </> val) accumulator = val; switch (that.type.getTag()) { case INT: decl.init = treeutils.makeIntLiteral(that.pos, that.op == JmlTokenKind.BSMAX ? Integer.MIN_VALUE : Integer.MAX_VALUE); break; case LONG: decl.init = treeutils.makeLit(that.pos, that.type, (that.op == JmlTokenKind.BSMAX ? Long.MIN_VALUE : Long.MAX_VALUE)); break; case SHORT: decl.init = treeutils.makeLit(that.pos, that.type, (that.op == JmlTokenKind.BSMAX ? Short.MIN_VALUE : Short.MAX_VALUE)); break; case BYTE: decl.init = treeutils.makeLit(that.pos, that.type, (that.op == JmlTokenKind.BSMAX ? Byte.MIN_VALUE : Byte.MAX_VALUE)); break; case CHAR: decl.init = treeutils.makeLit(that.pos, syms.intType, (int)(that.op == JmlTokenKind.BSMAX ? Character.MIN_VALUE : Character.MAX_VALUE)); break; case DOUBLE: decl.init = treeutils.makeLit(that.pos, that.type, (that.op == JmlTokenKind.BSMAX ? Double.MIN_VALUE : Double.MAX_VALUE)); break; case FLOAT: decl.init = treeutils.makeLit(that.pos, that.type, (that.op == JmlTokenKind.BSMAX ? Float.MIN_VALUE : Float.MAX_VALUE)); break; default: log.note(that,"rac.not.implemented.quantified"); throw new JmlNotImplementedException(that,"RAC not implemented for this type: " + that.type); } // FIXME - what about \bigint? should \min and \max be undefined if the range is empty? JCExpression tmp = !splitExpressions? newTemp(val) : val; // Make an ID if not already st = treeutils.makeAssignStat(that.pos,id,tmp); st = M.at(that.pos).If(treeutils.makeBinary(that.pos, that.op == JmlTokenKind.BSMAX ? JCTree.Tag.LT : JCTree.Tag.GT, idd, tmp), st, null); break; default: popBlock(0,that); // ignore popBlock(0,that); // ignore String msg = "Unknown quantified expression operation: " + that.op.internedName(); log.error(that,"jml.internal",msg); throw new JmlNotImplementedException(that,msg); } addStat(st); } finally { bl = popBlock(0L,that); // end of guarded block } st = M.If(guard, bl, null); addStat(st); } finally { if (bound.decl.type.getTag() == TypeTag.BOOLEAN) { // index = false; do { <innercomputation>; index = !index } while (index); st = treeutils.makeAssignStat(that.pos, treeutils.makeIdent(that.pos, indexdef.sym), treeutils.makeNot(that.pos, treeutils.makeIdent(that.pos, indexdef.sym))); addStat(st); bl = popBlock(0L,that); // loop block indexdef.init = treeutils.falseLit; JCExpression comp = treeutils.makeIdent(that.pos, indexdef.sym); addStat(indexdef); st = M.at(that.pos).DoLoop(bl,comp); if (brStat != null) brStat.target = st; st = M.at(that.pos).Labelled(label,st); addStat(st); } else if (bound.decl.type.getTag() == TypeTag.CLASS) { // for (T o: container) { <innercomputation>; } bl = popBlock(0L,that); // loop block st = M.at(that.pos).ForeachLoop(indexdef,bound.lo,bl); if (brStat != null) brStat.target = st; st = M.at(that.pos).Labelled(label,st); addStat(st); } else { // T index = lo; while (index </<= hi) { <inner computation>; index = index + 1 } st = treeutils.makeAssignStat(that.pos, treeutils.makeIdent(that.pos, indexdef.sym), treeutils.makeBinary(that.pos, JCTree.Tag.PLUS, treeutils.makeIdent(that.pos, indexdef.sym), treeutils.one)); // FIXME - one above might have to be long or bigint addStat(st); bl = popBlock(0L,that); // loop block indexdef.init = convertExpr(bound.lo); addStat(indexdef); JCExpression hi = convertExpr(bound.hi); JCExpression comp = treeutils.makeBinary(that.pos, bound.hi_equal ? JCTree.Tag.LE : JCTree.Tag.LT, bound.hi_equal ? treeutils.intleSymbol : treeutils.intltSymbol, treeutils.makeIdent(that.pos, indexdef.sym), hi); st = M.at(that.pos).WhileLoop(comp,bl); if (brStat != null) brStat.target = st; st = M.at(that.pos).Labelled(label,st); addStat(st); } } } } finally { addStat(popBlock(0L,that)); // pops enclosing block } result = eresult = treeutils.makeIdent(that.pos, decl.sym); } } finally { for (JCVariableDecl d: that.decls) { localVariables.remove(d.sym); } } return; } // FIXME - duplicate with what is in JmlAttr? protected static class Bound { public JCVariableDecl decl; public JCExpression lo; public JCExpression hi; boolean lo_equal; boolean hi_equal; JCVariableDecl indexdef; /*@Nullable*/JCVariableDecl lodef; /*@Nullable*/JCVariableDecl hidef; } /** If appropriate bounds can be determined for all defined variables, the method returns the * remaining expression and fills in the Bound list (first element is innermost loop); if appropriate * bounds cannot be determined, the method returns null. */ public JCExpression determineRacBounds(List<JCVariableDecl> decls, JCExpression range, java.util.List<Bound> bounds) { // Some current assumptions if (decls.length() != 1) return null; // FIXME - does only one declaration!!!!!! if (decls.head.type.getTag() == TypeTag.DOUBLE) return null; if (decls.head.type.getTag() == TypeTag.FLOAT) return null; if (decls.head.type.getTag() == TypeTag.BOOLEAN) { Bound b = new Bound(); b.decl = decls.head; b.lo = null; b.hi = null; bounds.add(0,b); return range; } else if (decls.head.type.getTag() == TypeTag.CLASS) { if (range instanceof JCBinary && ((JCBinary)range).getTag() != JCTree.Tag.AND) return null; JCExpression check = range instanceof JCBinary? ((JCBinary)range).lhs : range; if (!(check instanceof JCMethodInvocation)) return null; JCMethodInvocation mi = (JCMethodInvocation)check; if (!(mi.meth instanceof JCFieldAccess)) return null; JCFieldAccess fa = (JCFieldAccess)mi.meth; if (!fa.name.toString().equals("contains") && !fa.name.toString().equals("has")) return null; Bound b = new Bound(); b.decl = decls.head; b.lo = fa.selected; // FIXME - should check whether fa.selected is Iterable b.hi = null; bounds.add(0,b); return check == range ? check : // FIXME - could be set to true ((JCBinary)range).rhs; } try { // presume int JCBinary locomp = (JCBinary)((JCBinary)range).lhs; JCBinary hicomp = (JCBinary)((JCBinary)range).rhs; if (locomp.getTag() == JCTree.Tag.AND) { hicomp = (JCBinary)locomp.rhs; locomp = (JCBinary)locomp.lhs; } else if (hicomp.getTag() == JCTree.Tag.AND) { hicomp = (JCBinary)hicomp.lhs; } Bound b = new Bound(); b.decl = decls.head; JCTree.Tag tag = locomp.getTag(); if (tag == JCTree.Tag.GE || tag == JCTree.Tag.GT) b.lo = locomp.rhs; else b.lo = locomp.lhs; b.lo_equal = tag == JCTree.Tag.LE || tag == JCTree.Tag.GE; tag = hicomp.getTag(); if (tag == JCTree.Tag.GE || tag == JCTree.Tag.GT) b.hi = hicomp.lhs; else b.hi = hicomp.rhs; b.hi_equal = tag == JCTree.Tag.LE || tag == JCTree.Tag.GE; bounds.add(0,b); } catch (Exception e) { return null; } return range; } // OK @Override public void visitJmlSetComprehension(JmlSetComprehension that) { if (pureCopy || esc) { result = eresult = M.at(that). JmlSetComprehension(convert(that.newtype),convert(that.variable),convertExpr(that.predicate)).setType(that.type); return; } // FIXME - implement throw new JmlNotImplementedException(that,"set comprehension expression"); //return; } // OK @Override public void visitJmlSingleton(JmlSingleton that) { if (pureCopy) { JmlSingleton e = M.at(that).JmlSingleton(that.token); e.type = that.type; //e.symbol = that.symbol; e.info = that.info; treeutils.copyEndPosition(e,that); result = eresult = e; return; } if (!translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlSingleton: " + that.getClass()); return; } switch (that.token) { case BSRESULT: if (resultExpr != null) { eresult = convertCopy(resultExpr); eresult.pos = that.pos; } else { // This should be caught in type checking error(that, "It appears that \\result is used where no return value is defined"); } break; case INFORMAL_COMMENT: eresult = treeutils.makeBooleanLiteral(that.pos,true); break; case BSEXCEPTION: if (exceptionSym == null) { error(that,"It appears that \\exception is used where no exception value is defined" ); } else { // This should be caught in type checking eresult = treeutils.makeIdent(that.pos,exceptionSym); } break; case BSINDEX: if (indexStack.isEmpty()) { error(that,"\\index expression used outside a loop"); } else { // Get the index of the inner most loop JCVariableDecl vd = indexStack.get(0); JCIdent id = treeutils.makeIdent(that.pos,vd.sym); eresult = id; } break; // FIXME - implement these // case BSVALUES: // if (that.info == null) { // log.error(that.pos,"esc.internal.error","No loop index found for this use of " + that.token.internedName()); // result = null; // } else { // result = newIdentUse((VarSymbol)that.info,that.pos); // } // break; // // case BSLOCKSET: // case BSSAME: case BSNOTSPECIFIED: case BSNOTHING: case BSEVERYTHING: eresult = that; if (fullTranslation) { JmlSingleton e = M.at(that).JmlSingleton(that.token); e.info = that.info; //e.symbol = that.symbol; e.setType(that.type); eresult = e; } break; default: Log.instance(context).error(that.pos, "jml.unknown.construct",that.token.internedName(),"JmlAssertionAdder.visitJmlSingleton"); eresult = treeutils.makeZeroEquivalentLit(that.pos,that.type); // Not sure continuation will be successful in any way break; } result = eresult; } @Override public void visitJmlSpecificationCase(JmlSpecificationCase that) { if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlSpecificationCase while translating JML: " + that.getClass()); return; } if (!pureCopy) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlSpecificationCase: " + that.getClass()); return; } JmlSpecificationCase sc = M.at(that).JmlSpecificationCase( convert(that.modifiers), that.code, that.token, that.also, that.clauses); sc.sourcefile = that.sourcefile; sc.block = that.block == null ? null : convertBlock(that.block); // model program sc.setType(that.type); result = sc; } @Override public void visitJmlStatement(JmlStatement that) { result = null; switch (that.token) { case DEBUG: // FIXME - resolve what sort of constructs are actually allowed in a debug and set statement Set<String> keys = utils.commentKeys; if (!keys.contains("DEBUG")) return; //$FALL-THROUGH$ case SET: try { if (!pureCopy) addTraceableComment(that); // after the check on the key JCExpressionStatement st = that.statement; // (almost) Duplicated from visitExec JCExpression arg = convertJML(st.getExpression()); if (arg instanceof JCMethodInvocation || arg instanceof JCAssign || arg instanceof JCAssignOp || arg instanceof JCUnary) { result = addStat( M.at(that).Exec(arg).setType(that.type) ); } } catch (NoModelMethod e) { // Ignore - don't add a statement } catch (JmlNotImplementedException e) { notImplemented(that.token.internedName() + " statement containing ",e); result = null; } break; default: String msg = "Unknown token in JmlAssertionAdder.visitJmlStatement: " + that.token.internedName(); error(that, msg); } } // jmlrewriter? FIXME; also explain what this is @Override public void visitJmlStatementDecls(JmlStatementDecls that) { if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlStatementDecls while translating JML: " + that.getClass()); return; } for (JCStatement stat: that.list) { stat.accept(this); } // FIXME - result = ? } // OK @Override public void visitJmlStatementExpr(JmlStatementExpr that) { if (pureCopy) { JmlStatementExpr st = M.at(that).JmlExpressionStatement(that.token,that.label,convertExpr(that.expression)); st.setType(that.type); st.associatedSource = that.associatedSource; st.associatedPos = that.associatedPos; st.optionalExpression = convertExpr(that.optionalExpression); st.source = that.source; st.description = that.description; st.id = that.id; result = addStat(st); return; } boolean pv = checkAccessEnabled; // Don't check access during JML statements checkAccessEnabled = false; try { switch (that.token) { case ASSERT: addTraceableComment(that); JCExpression e = convertJML(that.expression); e = addImplicitConversion(that, syms.booleanType, e); addAssumeCheck(that,currentStatements,Strings.beforeAssertAssumeCheckDescription); JCExpression opt = that.optionalExpression; if (opt != null) { if (!(opt instanceof JCLiteral)) opt = convertJML(opt); if (rac) { JCExpression o = treeutils.convertToString(opt); if (o != null) opt = o; } } result = addAssert(false,that,Label.EXPLICIT_ASSERT,e,null,null,opt); break; case ASSUME: addTraceableComment(that); JCExpression ee = convertJML(that.expression); ee = addImplicitConversion(that, syms.booleanType, ee); opt = that.optionalExpression; if (!(opt instanceof JCLiteral)) { opt = convertJML(opt); } result = addAssume(that,Label.EXPLICIT_ASSUME,ee,null,null,opt); addAssumeCheck(that,currentStatements,Strings.afterAssumeAssumeCheckDescription); break; case COMMENT: JCExpression expr = fullTranslation ? convertJML(that.expression) : that.expression; { JmlStatementExpr st = M.at(that).JmlExpressionStatement(that.token,that.label,expr); st.setType(that.type); st.associatedSource = that.associatedSource; st.associatedPos = that.associatedPos; st.optionalExpression = fullTranslation ? convertExpr(that.optionalExpression) : that.optionalExpression; st.source = that.source; result = addStat(st); } break; case UNREACHABLE: addTraceableComment(that); result = addAssert(that,Label.UNREACHABLE, that.expression == null ? treeutils.falseLit : treeutils.makeNot(that.pos, convertJML(that.expression))); break; case REACHABLE: addTraceableComment(that); addAssumeCheck(that,currentStatements,Strings.atReachableStatementAssumeCheckDescription, that.expression == null ? treeutils.trueLit : convertExpr(that.expression)); break; case HENCE_BY: // FIXME - implement HENCE_BY notImplemented(that,"hence_by statement"); result = null; break; default: error(that,"Unknown token type in JmlAssertionAdder.visitJmlStatementExpr: " + that.token.internedName()); break; } } catch (JmlNotImplementedException e) { notImplemented(that.token.internedName() + " statement containing ",e); } finally { checkAccessEnabled = pv; } } // OK @Override public void visitJmlStatementHavoc(JmlStatementHavoc that) { if (translatingJML) { error(that,"Unexpected call of JmlAssertionAdder.visitJmlStatementHavoc while translating JML: " + that.getClass()); return; } try { result = addStat( M.at(that).JmlHavocStatement(convertJML(that.storerefs)).setType(that.type)); } catch (JmlNotImplementedException e) { notImplemented("havoc statement containing ",e); } } // OK @Override public void visitJmlStatementLoop(JmlStatementLoop that) { JmlStatementLoop st = M.at(that).JmlStatementLoop(that.token, convertExpr(that.expression)); st.setType(that.type); //st.sym = that.sym; result = st; } // OK @Override public void visitJmlStatementSpec(JmlStatementSpec that) { log.warning(that,"jml.refining.specs.not.implemented"); //result = M.at(that).JmlStatementSpec(convert(that.statementSpecs)).setType(that.type); } // OK @Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) { if (that.lo == that.hi && that.lo != null) { JCExpression ta = convertExpr(that.expression); JCExpression tl = convertExpr(that.lo); JmlBBArrayAccess aa = new JmlBBArrayAccess(null,ta,tl); aa.pos = that.pos; aa.type = that.type; result = eresult = aa; } else { JCExpression ta = convertExpr(that.expression); JCExpression tl = convertExpr(that.lo); result = eresult = M.at(that).JmlStoreRefArrayRange( ta, tl, (that.lo == that.hi) ? tl : convertExpr(that.hi) ).setType(that.type); } } // OK @Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { eresult = that; if (fullTranslation) eresult = M.at(that).JmlStoreRefKeyword(that.token); result = eresult; } // OK @Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { if (pureCopy) { result = eresult = M.at(that).JmlStoreRefListExpression(that.token,convert(that.list)).setType(that.type); return; } switch (that.token){ case BSNOTMODIFIED: { JCExpression and = null; for (JCExpression arg: that.list) { // FIXME - use past instead of old? // FIXME - what prestate should we refer to - e.g. refining statements and loops will have a different one JCIdent earlierState = preLabel; JCBinary bin; if (arg instanceof JCIdent) { JCExpression copy = convertCopy(arg); JCExpression old = treeutils.makeOld(arg.pos, copy, earlierState); if (rac) old = convertJML(old); bin = treeutils.makeEquality(arg.pos, arg, old); } else if (arg instanceof JCFieldAccess && ((JCFieldAccess)arg).name != null) { JCExpression copy = convertCopy(arg); JCExpression old = treeutils.makeOld(arg.pos, copy, earlierState); bin = treeutils.makeEquality(arg.pos, arg, old); } else if (arg instanceof JmlStoreRefArrayRange) { // Apparently even single indexes are parsed into ranges JmlStoreRefArrayRange ar = (JmlStoreRefArrayRange)arg; if (ar.lo == ar.hi) { JCExpression expr = M.at(arg.pos).Indexed(ar.expression,ar.lo); expr.type = arg.type; JCExpression copy = convertCopy(expr); JCExpression old = treeutils.makeOld(arg.pos, copy, earlierState); if (rac) old = convertJML(old); bin = treeutils.makeEquality(arg.pos, expr, old); } else { throw new JmlNotImplementedException(that,that.token.internedName()); } } else { // could be a.*, a[*], a[i..j] throw new JmlNotImplementedException(that,that.token.internedName()); } and = and == null ? bin : treeutils.makeAnd(that.pos, and, bin); } result = eresult = convertExpr(and); break; } default: result = eresult = M.at(that).JmlStoreRefListExpression(that.token,convert(that.list)).setType(that.type); } } // OK - readable and writable clauses @Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCIdent id = treeutils.makeIdent(that.identifier.pos,that.identifier.sym); JCExpression expr = convertExpr(that.expression); JmlTypeClauseConditional cl = M.at(that).JmlTypeClauseConditional(mods, that.token, id, expr); cl.setType(that.type); cl.source = that.source; classDefs.add(cl); result = cl; } // OK @Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCExpression expr = convertExpr(that.expression); JmlTypeClauseConstraint cl = M.at(that).JmlTypeClauseConstraint(mods, expr, convert(that.sigs)); cl.setType(that.type); cl.source = that.source; cl.token = that.token; cl.notlist = that.notlist; classDefs.add(cl); result = cl; } // OK - e.g. ghost or model declaration @Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { if (pureCopy) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JmlTypeClauseDecl cl = M.at(that).JmlTypeClauseDecl(convert(that.decl)); cl.setType(that.type); cl.source = that.source; cl.modifiers = mods; cl.token = that.token; classDefs.add(cl); result = cl; return; } scan(that.decl); } // OK - e.g. invariant @Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCExpression expr = convertExpr(that.expression); JmlTypeClauseExpr cl = M.at(that).JmlTypeClauseExpr(mods, that.token, expr); cl.setType(that.type); cl.source = that.source; // if (!rac) classDefs.add(cl);// FIXME - should we have this at all? result = cl; } // OK @Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JmlTypeClauseIn cl = M.at(that).JmlTypeClauseIn(convert(that.list)); cl.modifiers = mods; cl.setType(that.type); cl.source = that.source; cl.token = that.token; cl.parentVar = that.parentVar; // FIXME - need to map declaration classDefs.add(cl); result = cl; } // OK @Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JmlTypeClauseInitializer cl = M.at(that).JmlTypeClauseInitializer(that.token,mods); cl.specs = convert(that.specs); cl.setType(that.type); cl.source = that.source; classDefs.add(cl); result = cl; } // OK @Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCExpression expr = convertExpr(that.expression); JmlTypeClauseMaps cl = M.at(that).JmlTypeClauseMaps(expr,convert(that.list)); cl.modifiers = mods; cl.setType(that.type); cl.source = that.source; cl.token = that.token; classDefs.add(cl); result = cl; } // OK @Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCIdent id = treeutils.makeIdent(that.identifier.pos,that.identifier.sym); JmlTypeClauseMonitorsFor cl = M.at(that).JmlTypeClauseMonitorsFor(mods, id, convert(that.list)); cl.setType(that.type); cl.source = that.source; cl.token = that.token; classDefs.add(cl); result = cl; } // FIXME - needs review @Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { // FIXME - implement for esc if (pureCopy) { JCModifiers mods = fullTranslation ? convert(that.modifiers) : that.modifiers; JCExpression id = convertExpr(that.ident); JCExpression expr = convertExpr(that.expression); JmlTypeClauseRepresents cl = M.at(that).JmlTypeClauseRepresents(mods, id, that.suchThat, expr); cl.setType(that.type); cl.source = that.source; cl.token = that.token; classDefs.add(cl); result = cl; return; } JCExpression e = that.ident; Symbol sym = null; if (e instanceof JCIdent) sym = ((JCIdent)e).sym; else if (e instanceof JCFieldAccess) sym = ((JCFieldAccess)e).sym; else { // FIXME - this should really be in JmlAttr log.warning(that,"jml.internal.notsobad", "The lhs of a represents clause is expected to be an identifier or field access (found "+e.getClass()+")"); return; } if (rac && that.suchThat) { notImplemented(that,"relational represents clauses (\\such_that)", that.source()); return; } // The class we are in has a represents clause. // It may not have a corresponding model field; that field might be in a super class. // If so, we need to construct the synthetic model metehod to hold it. JmlSpecs.TypeSpecs typeSpecs = specs.getSpecs(classDecl.sym); if (sym != null) { String str = Strings.modelFieldMethodPrefix + sym.name.toString(); Name name = names.fromString(str); JmlMethodDecl mdecl = null; // Find the method for this model field. It will have been created in // JmlMemberEnter for (JmlTypeClauseDecl m: typeSpecs.modelFieldMethods) { JmlMethodDecl md = (JmlMethodDecl)m.decl; if (! md.name.toString().equals(str)) continue; // try { // JCStatement firststat = md.body.stats.head; // if (firststat instanceof JCThrow) { // // No (non-such_that) represents clause // // But still keep the model field's method // classDefs.add(md); // } else if (firststat instanceof JCReturn && ((JCReturn)firststat).expr == that.expression) { // pushBlock(); // boolean save = splitExpressions; // splitExpressions = false; // try { // JCReturn st = M.at(that).Return(convertExpr(that.expression)); // FIXME - what do we do about undefined expressions? // addStat(st); // md.body.stats = popBlock(0L,that.expression).stats; // md.mods.flags &= ~Flags.DEFAULT; // } finally { // classDefs.add(md); // splitExpressions = save; // } // } else { // log.warning(that.pos,"jml.duplicate.represents"); // } // // } catch (JmlNotImplementedException ee) { // // Can't implement this represents clause because // // of some non-translatable expression within it // notImplemented(that.token.internedName() + " clause containing ", ee, that.source()); // } mdecl = md; break; } if (mdecl == null) { // We can get here // when a subclass has a represents clause for a super class's // model field. long flags = Flags.PUBLIC | Flags.SYNTHETIC; flags |= (that.modifiers.flags & Flags.STATIC); JCModifiers mods = M.Modifiers(flags); JCMethodDecl msdecl = treeutils.makeMethodDefNoArg(mods,name,that.ident.type,classDecl.sym); msdecl.pos = that.pos; pushBlock(); try { currentStatements.addAll(msdecl.body.stats); // Note: we do not call convertJML on that expression because the body is converted // later on - for rac, in visitClassDecl we are handling represents clauses before we process // all the definitions in the class. // If we did call convertJML we would need to set the methodDecl so that newly created // variables are owned by the method and not by the class (which would causes NoSuchFieldError // when executed) JCReturn st = M.Return(that.expression); currentStatements.add(st); } catch (JmlNotImplementedException ee) { // Can't implement this represents clause because // of some non-translatable expression within it notImplemented(that.token.internedName() + " clause containing ", ee, that.source()); } finally { msdecl.body.stats = popBlock(0L,msdecl.body).stats; } classDefs.add(msdecl); JmlTypeClauseDecl tcd = M.JmlTypeClauseDecl(msdecl); tcd.modifiers = msdecl.mods; tcd.pos = msdecl.pos; tcd.source = that.source(); tcd.modifiers = msdecl.mods; // FIXME - is this necesssary typeSpecs.modelFieldMethods.append(tcd); // typeSpecs.decls.append(tcd); } } result = null; } public Type mathType(Type type) { TypeTag tag = type.getTag(); if (tag == TypeTag.FLOAT || tag == TypeTag.DOUBLE) return jmltypes.REAL; if (tag.ordinal() <= TypeTag.LONG.ordinal() && tag.ordinal() >= TypeTag.BYTE.ordinal()) return jmltypes.BIGINT; return type; } // FIXME - needs review @Override public void visitJmlVariableDecl(JmlVariableDecl that) { JavaFileObject prevSource = log.useSource(that.source()); try { if (pureCopy) { JCStatement stat = M.at(that).VarDef(that.sym,convertExpr(that.init)); if (inClassDecl()) classDefs.add(stat); else addStat(stat); result = stat; return; } JCIdent newident = null; if (esc) { that.ident = treeutils.makeIdent(that.pos,that.sym); // We don't use convertExpr, because that might chang the JCIdent to a JCFieldAccess newident = treeutils.makeIdent(that.pos, that.sym); exprBiMap.put(that.ident,newident); if (currentArithmeticMode instanceof Arithmetic.Math) { Symbol sym = that.sym; Type t = mathType(sym.type); if (t != sym.type){ Symbol newsym = sym.clone(sym.owner); newsym.type = t; newident.type = t; newident.sym = newsym; putSymbol(sym,newsym); } } } else if (rac) { // FIXME - should alo be copyint the symbol if (that.type instanceof JmlType) {// FIXME - should really be copying the AST that.vartype = ((JmlPrimitiveTypeTree)that.vartype).repType; that.type = jmltypes.repSym((JmlType)that.type).type; that.sym.type = that.type; } if (specs.fieldSpecHasAnnotation(that.sym, JmlTokenKind.SPEC_PUBLIC) != null) { that.mods.flags &= ~Flags.AccessFlags; that.mods.flags |= Flags.PUBLIC; that.sym.flags_field &= ~Flags.AccessFlags; that.sym.flags_field |= Flags.PUBLIC; } if (specs.fieldSpecHasAnnotation(that.sym, JmlTokenKind.SPEC_PROTECTED) != null) { that.mods.flags &= ~Flags.AccessFlags; that.mods.flags |= Flags.PROTECTED; that.sym.flags_field &= ~Flags.AccessFlags; that.sym.flags_field |= Flags.PROTECTED; } } if (!inClassDecl()) { addTraceableComment(that,that,that.toString(),null); } if (( that.type.tsym.flags_field & Flags.ENUM)!= 0) { // FIXME - should check the initializer expressions of enums JmlVariableDecl stat = M.at(that).VarDef(that.sym,that.init); stat.ident = newident; if (inClassDecl()) classDefs.add(stat); else addStat(stat); result = stat; return; } boolean inClassDecl = inClassDecl(); if (localVariables.containsKey(that.sym)) { JmlVariableDecl stat = M.at(that).VarDef(that.sym,null); stat.ident = newident; if (inClassDecl) classDefs.add(stat); else addStat(stat); result = stat; return; } // Called during translation of model methods if (JmlAttr.instance(context).isGhost(that.mods)) { try { JCExpression init = null; // If we are in a class, there is nowhere to push statements // so we make a block and later turn it into an initializer block if (inClassDecl) { pushBlock(); } JmlVariableDecl stat = null; boolean pv = checkAccessEnabled = checkAccessEnabled; checkAccessEnabled = false; try { init = convertJML(that.init); if (init != null) init = addImplicitConversion(init,that.type,init); stat = M.at(that).VarDef(that.sym,init); stat.ident = newident; JCExpression nn = null; if (init != null && !init.type.isPrimitive() && specs.isNonNull(that.sym,that.sym.enclClass())) { nn = treeutils.makeNeqObject(init.pos, init, treeutils.nullLit); if (init instanceof JCLiteral) { // FIXME - potential optimizations, but they need testing, particularly the second one if (init.type.getTag() == TypeTag.BOT) nn = treeutils.falseLit; else if (init.type.getTag() == TypeTag.CLASS) nn = treeutils.trueLit; } // FIXME - should have an associated position in assert } if (inClassDecl) methodDecl = (JmlMethodDecl)M.MethodDef(attr.makeInitializerMethodSymbol(that.mods.flags, JmlEnter.instance(context).getEnv(classDecl.sym)), null); if (nn != null) addAssert(that,Label.POSSIBLY_NULL_INITIALIZATION,nn,that.name); if (esc && !that.type.isPrimitive()) { addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(that, treeutils.makeIdent(that.pos, that.sym), that.type)); } } finally { if (inClassDecl) { JCBlock bl = popBlock(that.mods.flags & Flags.STATIC,that); if (stat != null) classDefs.add(stat); classDefs.add(bl); methodDecl = null; } else { if (stat != null) addStat(stat); } checkAccessEnabled = pv; } result = stat; } catch (JmlNotImplementedException e) { notImplemented("ghost declaration containing ",e); } } else if (that.init == null) { JmlVariableDecl stat = M.at(that).VarDef(that.sym,that.init); stat.ident = newident; // type, vartype, sym, name, mods, init are filled in stat.mods = that.mods; stat.sourcefile = that.sourcefile; stat.docComment = that.docComment; stat.fieldSpecs = that.fieldSpecs; // TOOD: copy? stat.fieldSpecsCombined = that.fieldSpecsCombined;// TODO: copy? stat.specsDecl = that.specsDecl; // TODO: copy? translate reference? if (!pureCopy) { if (currentStatements == null) classDefs.add(stat); else addStat(stat); } if (esc && !that.type.isPrimitive()) { addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(that, treeutils.makeIdent(that.pos, that.sym), that.type)); } result = stat; } else { // FIXME - are these regular Java declarations? what about model decls? // FIXME - need to make a unique symbol; what if the decl is final? JmlVariableDecl stat = M.at(that).VarDef(that.sym,null); stat.ident = newident; // type, vartype, sym, name, mods, init are filled in stat.mods = that.mods; stat.sourcefile = that.sourcefile; stat.docComment = that.docComment; stat.fieldSpecs = that.fieldSpecs; stat.fieldSpecsCombined = that.fieldSpecsCombined; stat.specsDecl = that.specsDecl; ListBuffer<JCStatement> check = pushBlock(); JCExpression init = null; JCExpression nn = null; try { if (statementStack.get(0) == null && methodDecl == null) { long flags = that.mods.flags & Flags.STATIC; // We are in an initializer block // We need a method symbol to be the owner of declarations // (otherwise they will have the class as owner and be thought to // be fields) MethodSymbol msym = new MethodSymbol( flags, classDecl.name, null, classDecl.sym); methodDecl = //M.MethodDef(msym, null,null); new JmlMethodDecl( M.Modifiers(flags, M.Annotations(List.<com.sun.tools.javac.code.Attribute.Compound>nil())), classDecl.name, null, null, null, null, null, null, //body, null, msym); } boolean pv = checkAccessEnabled; checkAccessEnabled = enclosingMethod != null; // FIXME - decide how to handle initialization of class fields try { init = convertExpr(that.init); } finally { checkAccessEnabled = pv; } if (init != null) init = addImplicitConversion(init,that.type,init); if (init != null && !init.type.isPrimitive() && specs.isNonNull(that)) { // isNonNull returns false if that.type is primitive nn = treeutils.makeNeqObject(init.pos, init, treeutils.nullLit); if (init instanceof JCLiteral) { // FIXME - potential optimizations, but they need testing, particularly the second one if (init.type.getTag() == TypeTag.BOT) nn = treeutils.falseLit; else if (init.type.getTag() == TypeTag.CLASS) nn = treeutils.trueLit; } // FIXME - should have an associated position } } finally { if (statementStack.get(0) == null) { // Class definition if (currentStatements.isEmpty() && nn == null) { // Just a simple initialization since there is no nonnull check // and the init expression did not create any new statements popBlock(0,null,check); // Nothing present - just ignore the empty block stat.init = init; this.classDefs.add(stat); if (esc && !that.type.isPrimitive()) { addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(that, treeutils.makeIdent(that.pos, that.sym), that.type)); } } else { long flags = that.mods.flags & Flags.STATIC; // Create a initializer block if (nn != null) addAssert(that,Label.POSSIBLY_NULL_INITIALIZATION,nn,that.name); addStat(treeutils.makeAssignStat(that.pos, treeutils.makeIdent(that.pos, stat.sym), init)); if (esc && !that.type.isPrimitive()) { addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(that, treeutils.makeIdent(that.pos, that.sym), that.type)); } JCBlock bl = popBlock(flags,that,check); this.classDefs.add(stat); this.classDefs.add(bl); } methodDecl = null; } else { // Regular method body JCBlock bl = popBlock(0,that,check); currentStatements.addAll(bl.stats); if (nn != null) addAssert(that,Label.POSSIBLY_NULL_INITIALIZATION,nn,that.name); stat.init = init; addStat(stat); if (esc && !that.type.isPrimitive()) { addAssume(that,Label.IMPLICIT_ASSUME,treeutils.makeDynamicTypeInEquality(that, treeutils.makeIdent(that.pos, that.sym), that.type)); } } result = stat; } } } finally { log.useSource(prevSource); } } // FIXME - review for best implementeation and uniform use protected boolean inClassDecl() { return currentStatements == null; } // OK @Override public void visitJmlWhileLoop(JmlWhileLoop that) { if (pureCopy) { JCExpression e = convertExpr(that.cond); JmlWhileLoop loop = M.at(that).WhileLoop(e,null); try { treeMap.put(that, loop); JCStatement bl = convertIntoBlock(that.body,that.body); loop.body = bl; loop.setType(that.type); loop.loopSpecs = convertCopy(that.loopSpecs); result = loop; } finally { treeMap.remove(that); } addStat(loop); return; } /* loop_invariant INV * loop_variant VAR * label: while (COND) { * BODY * } * becomes * { * assert INV * label: while (true) { * havoc * assume INV * TEMP = VAR * ... statements from COND * if (!COND') { * assert INV; * break; * } * assert 0 <= TEMP * loop_bodyN: { * ... statements from BODY * ... continue --> break loop_bodyN; * ... break --> break; * } * assert INV * TEMP2 = VAR * assert TEMP2 < TEMP * } * // FIXME - if break exits still have to satisfy invariant, put the check here * } * */ // FIXME - need label for loop if any and the target mapping // Outer block to restrict scopes of temporaries pushBlock(); JCVariableDecl indexDecl = loopHelperDeclareIndex(that);; java.util.List<JCIdent> decreasesIDs = new java.util.LinkedList<JCIdent>(); // Create this early so it is available as a target JmlWhileLoop loop = M.at(that).WhileLoop(treeutils.trueLit,null); treeMap.put(that, loop); // Test that invariants hold before entering loop loopHelperInitialInvariant(that.loopSpecs); // New loop body pushBlock(); // Havoc all items that might be changed in the loop if (esc) { loopHelperHavoc(that.body,indexDecl,null,that.body,that.cond); } loopHelperAssumeInvariants(that.loopSpecs, decreasesIDs, that); // Compute the condition, recording any side-effects { addTraceableComment(that.cond,that.cond,"Loop test"); JCExpression cond = convertExpr(that.cond); // The exit block tests the condition; if exiting, it tests the // invariant and breaks. loopHelperMakeBreak(that.loopSpecs, cond, loop, that); } // Now in the loop, so check that the variants are non-negative loopHelperCheckNegative(decreasesIDs, that); // Then translate the original loop body // Have to do some footwork to get the Block object before constructing its contents loopHelperMakeBody(that.body); // increment the index loopHelperIncrementIndex(indexDecl); // After the loop, check the invariants and check that the variants have decreased loopHelperAssertInvariants(that.loopSpecs,decreasesIDs); // Finish up the new loop body // Finish up the output block loopHelperFinish(loop,that); } public static class PositionChecker extends JmlTreeScanner { int lo; int hi; Log log; EndPosTable table; public void check(Log log, JCTree tree) { lo = 0; hi = Integer.MAX_VALUE; this.log = log; table = log.currentSource().getEndPosTable(); scan(tree); } public void scan(JCTree tree) {// FIXME - we see a bad end position for JC/JmlMethodDecl, JCModifiers, JCPrimitiveTYpeTree if (tree == null) return; int sp = tree.getStartPosition(); int pp = tree.getPreferredPosition(); int ep = table.getEndPos(tree); PrintWriter noticeWriter = log.getWriter(WriterKind.NOTICE); noticeWriter.println(" POSITIONS: " + lo + " " + sp + " " + pp + " " + tree.pos + " " + ep + " " + hi + " " + tree); if (!(lo <= sp && sp <= pp && pp == tree.pos && pp <= ep && ep <= hi) && !(tree instanceof JCModifiers && sp == -1 && ep == -1)) { noticeWriter.println("BAD POSITIONS: " + lo + " " + sp + " " + pp + " " + tree.pos + " " + ep + " " + hi + " " + tree); sp = pp; } int savedlo = this.lo; int savedhi = this.hi; this.lo = sp; this.hi = ep; super.scan(tree); this.lo = savedlo; this.hi = savedhi; } } // public void record(JCTree that) { // idmapper.scan(that); // } // // final public IdentityMap idmapper = new IdentityMap(); // // // // Not static // public class IdentityMap extends JmlTreeScanner { // // public void scan(JCTree that) { // super.scan(that); // exprBiMap.put(that, that); // } // // } // // public static interface I { // public int v(final Object[] args); // } // // public static class A { // // // public int m() { // // Object o; // o = new Object(); // // int r = (new I(){ public int v(final Object[] args) { return 1; }}).v(new Object[]{o}); // return r; // } // } // protected void markLocation(Name label, ListBuffer<JCStatement> list, JCStatement marker) { Location loc = new Location(list,marker); locations.put(label,loc); } protected void insertAtLocation(Name label, JCStatement ... statements) { Location loc = locations.get(label); if (loc == null) { // INTENRAL ERROR - FIXME } else { ListBuffer<JCStatement> orig = loc.list; ListBuffer<JCStatement> temp = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> tail = new ListBuffer<JCStatement>(); head: { while (!orig.isEmpty()) { JCStatement t = orig.remove(); if (t == loc.marker) { tail.add(t); break head; } temp.add(t); } // FIXME - INTERNAL ERROR - marker not in list } while (!orig.isEmpty()) { JCStatement t = orig.remove(); tail.add(t); } orig.appendList(temp); orig.appendArray(statements); orig.appendList(tail); } } protected Map<Name, Location> locations = new HashMap<Name,Location>(); public class Location { public ListBuffer<JCStatement> list; public JCStatement marker; public Location(ListBuffer<JCStatement> list, JCStatement marker) { this.list = list; this.marker = marker; } } public class ParentItem { public ClassSymbol classSym; public Map<Type.TypeVar,Type> vars = new HashMap<Type.TypeVar,Type>(); public ParentItem(ClassSymbol cs, Map<Type.TypeVar,Type> vm) { classSym = cs; vars = vm; } } /** Returns a list of super classes and interfaces, as types; * the order is that interfaces come before classes and super classes/interfaces come before derived ones, * with the argument type last. */ public java.util.List<Type> parents(Type ct, boolean includeEnclosing) { // FIXME - not implemented for includeEnclosing = true // FIXME - unify this with the methods in Utils. java.util.List<Type> classes = new LinkedList<Type>(); Type cc = ct; while (cc != null && cc.getTag() != TypeTag.NONE) { classes.add(0,cc); if (cc instanceof Type.ClassType) { Type.ClassType cct = (Type.ClassType)cc; cc = cct.supertype_field; if (cc == null) cc = ((Type.ClassType)cct.tsym.type).supertype_field; } else if (cc instanceof Type.ArrayType) { cc = syms.objectType; } else if (cc instanceof Type.TypeVar) { cc = ((Type.TypeVar)cc).bound; } else { cc = null; } } java.util.List<Type> interfacesToDo = new LinkedList<Type>(); java.util.List<Type> interfaces = new LinkedList<Type>(); for (Type cty: classes) { if (!(cty instanceof Type.ClassType)) continue; Type.ClassType cct = (Type.ClassType)cty; List<Type> ifs = cct.interfaces_field; if (ifs == null) ifs = ((Type.ClassType)cct.tsym.type).interfaces_field; if (ifs != null) interfacesToDo.addAll(ifs); } x: while (!interfacesToDo.isEmpty()) { Type ifc = interfacesToDo.remove(0); for (Type t: interfaces) { if (types.isSameType(t, ifc)) continue x; } interfaces.add(0,ifc); List<Type> ints = ((Type.ClassType)ifc.tsym.type).interfaces_field; if (ints != null) for (Type fin: ints) { interfacesToDo.add(fin); } } Type obj = classes.remove(0); interfaces.addAll(classes); interfaces.add(0,obj); return interfaces; } public Map<TypeSymbol,Type> typemapping(Type ct, Symbol sym, List<JCExpression> typeargs) { return typemapping(ct,sym,typeargs,null); } public Map<TypeSymbol,Type> typemapping(Type ct, Symbol sym, List<JCExpression> typeargs, Type.MethodType methodType) { Map<TypeSymbol,Type> vars = new HashMap<TypeSymbol,Type>(); if (ct instanceof Type.ClassType){ Type ect = ct.getEnclosingType(); if (ect != null) vars = typemapping(ect, sym, typeargs, methodType); } if (ct instanceof Type.ClassType && !((Type.ClassType)ct).getTypeArguments().isEmpty()) { Iterator<Type> ity = ((Type.ClassType)ct).getTypeArguments().iterator(); for (TypeSymbol tsym: ct.tsym.getTypeParameters()) { Type cty = ity.next(); vars.put(tsym, cty); } } if (sym != null && sym.type instanceof Type.ForAll) { if (typeargs != null && !typeargs.isEmpty()) { List<Type> tlist = ((Type.ForAll)sym.type).tvars; Iterator<JCExpression> ita = typeargs.iterator(); for (Type t: tlist) { JCExpression tta = ita.next(); vars.put(t.tsym, tta.type); } } else if (methodType != null) { // Unify to find types // FIXME - just doing simple unification for now List<Type> tt = methodType.getParameterTypes(); List<Type> tlist = ((Type.ForAll)sym.type).getParameterTypes(); Iterator<Type> tlisti = tlist.iterator(); Iterator<Type> tti = tt.iterator(); while (tti.hasNext()) { Type t1 = tlisti.next(); Type t2 = tti.next(); if (t1 instanceof Type.TypeVar) vars.put(t1.tsym, t2); } } } return vars; } public java.util.List<Pair<MethodSymbol,Type>> parents(MethodSymbol m, Type classType) { java.util.List<Pair<MethodSymbol,Type>> methods = new LinkedList<Pair<MethodSymbol,Type>>(); if (utils.isJMLStatic(m)) { methods.add(pair(m,m.owner.type)); } else { for (Type c: parents(classType, false)) { for (Symbol mem: c.tsym.getEnclosedElements()) { if (mem instanceof MethodSymbol && mem.name.equals(m.name) && (mem == m || m.overrides(mem, c.tsym, Types.instance(context), true) || ((MethodSymbol)mem).overrides(m, (TypeSymbol)m.owner, Types.instance(context), true))) { methods.add(pair((MethodSymbol)mem,c)); } } } } return methods; } public static <F,S> Pair<F,S> pair(F f, S s) { return new Pair(f,s); } public static class Pair<F,S> { public F first; public S second; public Pair(F f, S s) { first = f; second = s; } } // public java.util.List<JCExpression> makeMethodAxioms(JmlMethodDecl methodDecl) { // // requires that method is pure // // create an axiom from specification case that is normal_behavior // // or has no signals clause, or a signals clause that is Exception false // java.util.List<JCExpression> axioms = new LinkedList<JCExpression>(); // for (MethodSymbol msym: utils.parents(methodDecl.sym)) { // JmlSpecs.MethodSpecs mspecs = specs.getSpecs(msym); // for (JmlSpecificationCase cs: mspecs.cases.cases) { // boolean makeAxiom = false; // if (cs.token == JmlToken.NORMAL_BEHAVIOR) makeAxiom = true; // //FIXME - check signals clauses? // // if (!makeAxiom) continue; // // // Make signature and call instance // // ListBuffer<JCVariableDecl> decls = new ListBuffer<JCVariableDecl>(); // ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); // Map<Object,JCExpression> substitutions = new HashMap<Object,JCExpression>(); // JmlTreeSubstitute subst = new JmlTreeSubstitute(context,M,substitutions); // TODO - use a factory? // // if (!msym.isStatic()) { // // Add in a slot for the receiver // } // for (VarSymbol vsym : msym.params) { // JCVariableDecl decl = treeutils.makeVariableDecl(vsym.name, vsym.type, null, cs.pos); // decl.sym.owner = methodDecl.sym; // decls.add(decl); // JCIdent id = M.at(decl.pos).Ident(decl.sym); // substitutions.put(decl.sym, id); // args.add(id); // } // // // Assemble pre and post condition // JCExpression pre = null; // JCExpression post = null; // for (JmlMethodClause cl : cs.clauses) { // if (cl.token == JmlToken.REQUIRES) { // JCExpression ex = ((JmlMethodClauseExpr)cl).expression; // ex = subst.copy(ex); // pre = pre == null ? ex : treeutils.makeAnd(pre.pos, pre, ex); // } // if (cl.token == JmlToken.ENSURES) { // JCExpression ex = ((JmlMethodClauseExpr)cl).expression; // ex = subst.copy(ex); // post = post == null ? ex : treeutils.makeAnd(post.pos, post, ex); // } // } // if (pre == null) pre = treeutils.trueLit; // if (treeutils.isFalseLit(pre)) continue; // trivial axiom // if (post == null || treeutils.isTrueLit(post)) continue; // trivial axiom // JCExpression ex = treeutils.makeImplies(cs.pos, pre, post); // // // Wrap in a forall // JmlQuantifiedExpr q = M.JmlQuantifiedExpr(JmlToken.FORALL, decls.toList(), null, ex); // q.pos = cs.pos; // q.type = syms.booleanType; // axioms.add(q); // // log.noticeWriter.println("For " + msym + " : " + q); // } // } // // // return axioms; // // } public static class WellDefined { public MethodSymbol sym; public JCExpression wellDefinedExpression; public DiagnosticPosition pos; public JavaFileObject source; public boolean alltrue; } /** Creates and adds to the current statements axioms for the method given by msym. * Suppose method M(T i) has a spec requires P; ensures Q;. Then we define a * function M(heap,i) and assume (for each postcondition) * (\forall T i; P; (M(heap,i) == Q)) and we define a well-definedness criterion * M_WD(heap,i) and assume (\forall T i; ; (M_WD(heap,i) == P)) */ int depth = 0; ListBuffer<JCStatement> savedForAxioms = null; protected JCBlock addMethodAxioms(DiagnosticPosition callLocation, MethodSymbol msym, java.util.List<Pair<MethodSymbol,Type>> overridden) { boolean isFunction = attr.isFunction(msym); if (!inOldEnv && !addAxioms(heapCount,msym)) { return M.at(Position.NOPOS).Block(0L, List.<JCStatement>nil()); } boolean isStatic = utils.isJMLStatic(msym); JCExpression savedResultExpr = resultExpr; JCIdent savedCurrentThisId = currentThisId; JCExpression savedCurrentThisExpr = currentThisExpr; Map<Object,JCExpression> savedParamActuals = paramActuals; pushBlock(); ListBuffer<JCStatement> saved = currentStatements; if (depth == 0) savedForAxioms = saved; depth++; JmlMethodSpecs calleeSpecs = specs.getDenestedSpecs(msym); // FIXME - we get calleeSpecs == null when using -no-internalSpecs - shoudl we? int pos = calleeSpecs != null && calleeSpecs.decl != null ? calleeSpecs.decl.pos : methodDecl.pos; Name newMethodName = names.fromString(utils.qualifiedName(msym).replace('.', '_') + "_" + pos); addStat(comment(callLocation,"Axioms for method " + utils.qualifiedMethodSig(msym),null)); JCExpression combinedPre = null; JCExpression falses = null; JmlMethodClause clauseToReference = null; try { // Construct the lists of parameters and parameter types for the logical function representing the pure function ListBuffer<JCVariableDecl> newDecls = new ListBuffer<JCVariableDecl>(); ListBuffer<JCExpression> newparamsWithHeap = new ListBuffer<JCExpression>(); if (!isFunction) { newparamsWithHeap.add(convert(treeutils.makeIdent(Position.NOPOS,heapSym))); } JCIdent qthisid = null; JCExpression qthisnn = null; if (!isStatic) { JCVariableDecl newDecl = treeutils.makeVarDef(currentThisExpr.type, names.fromString("QTHIS"), methodDecl.sym, pos); newDecls.add(newDecl); qthisid = treeutils.makeIdent(callLocation,newDecl.sym); newparamsWithHeap.add(qthisid); qthisnn = treeutils.makeNotNull(qthisid.pos, qthisid); } currentThisExpr = currentThisId = qthisid; if (calleeSpecs != null && calleeSpecs.decl != null) { for (JCVariableDecl d : specs.getDenestedSpecs(msym).decl.params) { JCVariableDecl newDecl = treeutils.makeVarDef(d.type, d.name, methodDecl.sym, d.pos); newDecls.add(newDecl); JCIdent id = treeutils.makeIdent(d,newDecl.sym); newparamsWithHeap.add(id); } } else if (msym.params != null){ for (VarSymbol d : msym.params) { JCVariableDecl newDecl = treeutils.makeVarDef(d.type, d.name, methodDecl.sym, methodDecl.pos); newDecls.add(newDecl); JCIdent id = treeutils.makeIdent(callLocation,newDecl.sym); newparamsWithHeap.add(id); } } else { // int i = 0; // for (VarSymbol d : msym.type.params) { // JCVariableDecl newDecl = treeutils.makeVarDef(d.type, names.fromString("ZZ" + (i++)), methodDecl.sym, methodDecl.pos); // newDecls.add(newDecl); // JCIdent id = treeutils.makeIdent(callLocation,newDecl.sym); // newparamsWithHeap.add(id); // } } List<JCVariableDecl> newDeclsList = newDecls.toList(); List<JCExpression> newParamsWithHeap = newparamsWithHeap.toList(); ListBuffer<Type> newparamtypes = new ListBuffer<Type>(); for (JCExpression e: newParamsWithHeap) { newparamtypes.add(e.type); } List<Type> newParamTypes = newparamtypes.toList(); // Create the symbol for the new method JCModifiers newmods = M.at(methodDecl.mods.pos).Modifiers(methodDecl.mods.flags | Flags.STATIC, methodDecl.mods.annotations); // Note: annotations not duplicated MethodSymbol newsym = treeutils.makeMethodSym(newmods, newMethodName, msym.getReturnType(), (TypeSymbol)methodDecl.sym.owner, newParamTypes); pureMethod.put(msym,newsym); // Now go through each spec case for each overridden method and construct axioms for each ensures clause for (Pair<MethodSymbol,Type> pair: overridden) { MethodSymbol mpsym = pair.first; Type classType = pair.second; typevarMapping = typemapping(classType, mpsym, null); // This initial logic must match that above for preconditions calleeSpecs = specs.getDenestedSpecs(mpsym); if (calleeSpecs == null) continue; // FIXME - not sure about this if (calleeSpecs.decl == null) continue; // Now map the formals as used in the overridden method to // identifiers used in the axioms being constructed paramActuals = new HashMap<Object,JCExpression>(); Iterator<JCVariableDecl> iter = newDeclsList.iterator(); currentThisExpr = currentThisId = isStatic ? null : M.at(calleeSpecs.decl).Ident(iter.next().sym); for (JCVariableDecl d : calleeSpecs.decl.params) { JCIdent id = treeutils.makeIdent(d,iter.next().sym); paramActuals.put(d.sym, id); } // Create an instance of a call of the new method, to be used in place of \result in translating the method specs JCExpression fcn = treeutils.makeIdent(Position.NOPOS,newsym); JCMethodInvocation call = M.at(Position.NOPOS).Apply( List.<JCExpression>nil(), fcn, newParamsWithHeap); call.type = newsym.getReturnType(); resultExpr = call; ListBuffer<JCStatement> ignore = currentStatements = new ListBuffer<JCStatement>(); // We capture and ignore the assertions that come out of // transforming the pre and post conditions of the callee. // These are checked when the method is verified. // Also they would need to be captured and proved in the // the context of a quantification. for (JmlSpecificationCase cs : calleeSpecs.cases) { // FIXME - jml visible? if (!utils.visible(classDecl.sym, mpsym.owner, cs.modifiers.flags/*, methodDecl.mods.flags*/)) continue; // FIXME - will need to add OLD and FORALL clauses in here JCExpression pre = qthisnn != null ? qthisnn : treeutils.trueLit; if (qthisid != null && classType != syms.objectType) { // FIXME - not sure this is needed and it makes valid assertions harder to prove pre = treeutils.makeInstanceOf(cs.pos,qthisid,classType); pre = convertCopy(pre); pre = treeutils.makeAnd(cs.pos, treeutils.makeNotNull(cs.pos, convertCopy(qthisid)), pre); } else { pre = convertCopy(pre); pre.pos = cs.pos; } try { JmlMethodClause mcc = null; // Remember the first requires clause in the specification case currentStatements = ignore; for (JmlMethodClause clause : cs.clauses) { try { if (clause.token == JmlTokenKind.REQUIRES) { if (mcc == null) mcc = clause; JmlMethodClauseExpr mce = (JmlMethodClauseExpr)clause; // convertJML will use paramActuals JCExpression e = convertJML(mce.expression,pre,false); // Might throw an exception pre = treeutils.makeAndSimp(pre.pos, pre, e); } } catch (JmlNotImplementedException e) { notImplemented("requires clause containing ",e, clause.source()); pre = treeutils.falseLit; break; } } if (mcc != null) clauseToReference = mcc; } catch (NoModelMethod e) { pre = treeutils.falseLit; } finally { currentStatements = savedForAxioms; } combinedPre = (combinedPre == null) ? convertCopy(pre) : treeutils.makeOrSimp(pre.pos, combinedPre, convertCopy(pre)); if (treeutils.isFalseLit(pre)) continue; // Don't bother with postconditions if corresponding precondition is explicitly false for (JmlMethodClause clause : cs.clauses) { DiagnosticPosition dpos = clause; // FIXME - clause.sourceFile should never be null? JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile; if (clause.token == JmlTokenKind.ENSURES) { //localVariables.put(heapSym,heapSym); // FIXME - explain try { currentStatements = ignore; //addStat(comment(clause)); // Note - convertJML uses resultExpr and currentThisExpr and paramActuals JCExpression e = convertJML(((JmlMethodClauseExpr)clause).expression, condition, false); if (treeutils.isFalseLit(e)) { JCExpression not = treeutils.makeNot(pre.pos, convertCopy(pre)); falses = falses == null ? not : treeutils.makeAnd(falses.pos, falses, not); continue; } if (newDeclsList.isEmpty()) { e = treeutils.makeImplies(e.pos, convertCopy(pre), e); } else { e = M.at(dpos).JmlQuantifiedExpr( JmlTokenKind.BSFORALL, newDeclsList, // FIXME - is it OK that we use the same copy of this array (and so the same symbols) for each postcondition expression // FIXME - if not, we have to conjoin all the ensures convertCopy(pre), e); e.type = syms.booleanType; } currentStatements = savedForAxioms; addAssume(dpos,Label.IMPLICIT_ASSUME,e,dpos,clauseSource); } catch (NoModelMethod e) { // Just skip this ensures clause } catch (JmlNotImplementedException e) { notImplemented(clause.token.internedName() + " clause containing ",e, clause.source()); } finally { currentStatements = savedForAxioms; //localVariables.remove(heapSym); } } } } paramActuals = null; } if (falses != null) { combinedPre = combinedPre != null ? treeutils.makeAnd(combinedPre.pos,combinedPre,falses) : falses; } currentStatements = savedForAxioms; if (combinedPre == null || treeutils.isTrueLit(combinedPre)) { WellDefined info = new WellDefined(); info.alltrue = true; info.wellDefinedExpression = treeutils.trueLit; wellDefinedCheck.put(msym, info); } else { MethodSymbol methodDefinedSym = treeutils.makeMethodSym(methodDecl.mods, names.fromString("_JML_METHODEF__" + newMethodName.toString()), syms.booleanType, classDecl.sym, newParamTypes); JCExpression methodDefinedFcn = M.at(pos).Ident(methodDefinedSym); JCMethodInvocation wellDefinedCall = M.at(pos).Apply( List.<JCExpression>nil(), methodDefinedFcn, newParamsWithHeap); wellDefinedCall.type = syms.booleanType; WellDefined info = new WellDefined(); info.sym = methodDefinedSym; info.wellDefinedExpression = combinedPre; info.pos = clauseToReference != null ? clauseToReference : methodDecl; info.source = clauseToReference != null ? clauseToReference.source() : methodDecl.source(); info.alltrue = false; wellDefinedCheck.put(msym, info); if (newDeclsList.isEmpty()) { addAssume(clauseToReference,Label.IMPLICIT_ASSUME,combinedPre); } else { JCExpression ei = null; // if (!qthisid.sym.type.isPrimitive()) { // ei = treeutils.makeInstanceOf(qthisid.pos,convertCopy(qthisid),qthisid.sym.type); // } JCExpression e = M.at(pos).JmlQuantifiedExpr( JmlTokenKind.BSFORALL, newDeclsList, ei, treeutils.makeEquality(pos, wellDefinedCall, combinedPre)); e.type = syms.booleanType; addAssume(clauseToReference,Label.IMPLICIT_ASSUME,e); } } } finally { resultExpr = savedResultExpr; currentThisId = savedCurrentThisId; currentThisExpr = savedCurrentThisExpr; paramActuals = savedParamActuals; currentStatements = saved; depth--; if (depth == 0) savedForAxioms = null; } return popBlock(0L,null); } public @Nullable java.util.List<JmlStatementExpr> getWellDefinedAsserts(JCExpression expr, Map<Object, JCExpression> replacements) { WellDefinedCheck check = new WellDefinedCheck(replacements); return expr.accept(check,null); } public class WellDefinedCheck extends JmlExpressionVisitor<java.util.List<JmlStatementExpr>,Void> { Map<Object, JCExpression> replacements; JmlTreeSubstitute sub; public WellDefinedCheck(Map<Object, JCExpression> replacements) { this.replacements = replacements; sub = new JmlTreeSubstitute(context,M,replacements); } @Nullable java.util.List<JmlStatementExpr> combine(@Nullable java.util.List<JmlStatementExpr> a, @Nullable java.util.List<JmlStatementExpr> b) { if (a == null) return b; if (b == null) return a; a.addAll(b); return a; } @Nullable java.util.List<JmlStatementExpr> combine(@Nullable java.util.List<JmlStatementExpr> a, JmlStatementExpr b) { if (a == null) a = new LinkedList<JmlStatementExpr>(); a.add(b); return a; } @Nullable java.util.List<JmlStatementExpr> prepend(JCExpression cond, @Nullable java.util.List<JmlStatementExpr> list) { for (JmlStatementExpr a: list) { a.expression = treeutils.makeImplies(a.expression.pos, cond, a.expression); } return list; } @Override public @Nullable java.util.List<JmlStatementExpr> visitBinary(BinaryTree that, Void p) { JCBinary bin = (JCBinary)that; JCExpression lhs = bin.lhs; JCExpression rhs = bin.rhs; JCTree.Tag op = bin.getTag(); @Nullable java.util.List<JmlStatementExpr> elhs = lhs.accept(this, p); @Nullable java.util.List<JmlStatementExpr> erhs = rhs.accept(this, p); @Nullable java.util.List<JmlStatementExpr> e = combine(elhs, erhs); if (op == JCTree.Tag.DIV) { JCExpression ee = treeutils.makeBinary(rhs.pos,JCTree.Tag.NE,treeutils.intneqSymbol,sub.copy(rhs),treeutils.zero).setType(syms.booleanType); JmlStatementExpr a = treeutils.makeAssert(rhs, Label.UNDEFINED_DIV0, ee); e = combine(e,a); } else if (op == JCTree.Tag.AND) { JCExpression copy = sub.copy(lhs); e = prepend(copy, e); } else if (op == JCTree.Tag.OR) { JCExpression copy = treeutils.makeNot(lhs.pos, sub.copy(lhs)); e = prepend(copy, e); } // FIXME - shifts, arithmetic overflow return e; } @Override public @Nullable java.util.List<JmlStatementExpr> visitLetExpr(LetExpr that, Void p) { ListBuffer<JCVariableDecl> newdecls = new ListBuffer<JCVariableDecl>(); ListBuffer<JCExpression> inits = new ListBuffer<JCExpression>(); @Nullable java.util.List<JmlStatementExpr> asserts = null; try { for (JCVariableDecl decl: that.defs) { @Nullable java.util.List<JmlStatementExpr> init = decl.init.accept(this,p); asserts = combine(asserts, init); inits.add( sub.copy(decl.init)); } for (JCVariableDecl decl: that.defs) { JCVariableDecl nd = treeutils.makeVarDef(decl.type,decl.name,decl.sym.owner,inits.remove()); newdecls.add(nd); JCExpression was = replacements.put(decl.sym, treeutils.makeIdent(decl.pos, nd.sym)); if (was != null) throw new RuntimeException("Same Decl in replacement map"); } @Nullable java.util.List<JmlStatementExpr> value = that.expr.accept(this,p); if (value != null) for (JmlStatementExpr a: value) { // FIXME - all the let expressions are using the same decls with the same symbols and same initializers LetExpr q = M.at(that.pos).LetExpr(newdecls.toList(), a.expression); q.setType(that.type); a.expression = q; } return combine(asserts, value); } finally { for (JCVariableDecl decl: that.defs) { replacements.remove(decl.sym); } } } @Override public @Nullable java.util.List<JmlStatementExpr> visitLambdaExpression(LambdaExpressionTree that, Void p) { // FIXME JCTree.JCLambda exp = (JCTree.JCLambda)that; notImplemented("Lambda Expression not implemented",null); return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitMethodInvocation(MethodInvocationTree node, Void p) { JCMethodInvocation that = (JCMethodInvocation)node; @Nullable java.util.List<JmlStatementExpr> wellDefined = that.meth.accept(this, p); for (JCExpression arg: that.args) { wellDefined = combine( wellDefined, arg.accept(this,p)); } // TODO Auto-generated method stub return wellDefined; } @Override public @Nullable java.util.List<JmlStatementExpr> visitClass(ClassTree node, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitConditionalExpression( ConditionalExpressionTree node, Void p) { JCConditional that = (JCConditional)node; JCExpression cond = that.cond; JCExpression lhs = that.truepart; JCExpression rhs = that.falsepart; @Nullable java.util.List<JmlStatementExpr> wellDefinedCond = cond.accept(this, p); @Nullable java.util.List<JmlStatementExpr> wellDefinedTrue = lhs.accept(this, p); @Nullable java.util.List<JmlStatementExpr> wellDefinedFalse = rhs.accept(this, p); JCExpression condTrue = sub.copy(cond); JCExpression condFalse = treeutils.makeNot(cond.pos,sub.copy(cond)); wellDefinedTrue = prepend(condTrue, wellDefinedTrue); wellDefinedFalse = prepend(condFalse, wellDefinedFalse); wellDefinedCond = combine(combine(wellDefinedCond, wellDefinedTrue), wellDefinedFalse); return wellDefinedCond; } @Override public @Nullable java.util.List<JmlStatementExpr> visitErroneous(ErroneousTree node, Void p) { return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitIdentifier(IdentifierTree node, Void p) { // FIXME - what if a model field // FIXME - non null ? return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitArrayAccess(ArrayAccessTree node, Void p) { JCArrayAccess aa = (JCArrayAccess)node; JCExpression array = aa.getExpression(); @Nullable java.util.List<JmlStatementExpr> wellDefined = array.accept(this,p); JCExpression index = aa.getIndex(); wellDefined = combine(wellDefined, index.accept(this, p)); JCExpression e = treeutils.makeNotNull(array.pos, sub.copy(array)); JmlStatementExpr a = treeutils.makeAssert(aa, Label.UNDEFINED_NULL_DEREFERENCE, e); wellDefined = combine(wellDefined, a); e = treeutils.makeBinary(index.pos, JCTree.Tag.LE, treeutils.intleSymbol, treeutils.zero, sub.copy(index)); a = treeutils.makeAssert(aa, Label.UNDEFINED_NEGATIVEINDEX, e); wellDefined = combine(wellDefined, a); e = treeutils.makeLength(aa, sub.copy(array)); e = treeutils.makeBinary(index.pos, JCTree.Tag.LT, treeutils.intltSymbol, sub.copy(index), e); a = treeutils.makeAssert(aa, Label.UNDEFINED_TOOLARGEINDEX, e); wellDefined = combine(wellDefined, a); return wellDefined; } @Override public @Nullable java.util.List<JmlStatementExpr> visitLiteral(LiteralTree node, Void p) { return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitNewArray(NewArrayTree node, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitNewClass(NewClassTree node, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitParenthesized(ParenthesizedTree node, Void p) { return node.getExpression().accept(this,p); } @Override public @Nullable java.util.List<JmlStatementExpr> visitMemberSelect(MemberSelectTree node, Void p) { JCFieldAccess fa = (JCFieldAccess)node; JCExpression lhs = fa.getExpression(); @Nullable java.util.List<JmlStatementExpr> wellDefinedLhs = fa.accept(this,p); JCExpression e = treeutils.makeNotNull(lhs.pos, sub.copy(lhs)); JmlStatementExpr a = treeutils.makeAssert(fa, Label.UNDEFINED_NULL_DEREFERENCE, e); return combine(wellDefinedLhs, a); } @Override public @Nullable java.util.List<JmlStatementExpr> visitTypeCast(TypeCastTree node, Void p) { // TODO Auto-generated method stub return node.getExpression().accept(this,p); } @Override public @Nullable java.util.List<JmlStatementExpr> visitInstanceOf(InstanceOfTree node, Void p) { // TODO Auto-generated method stub return node.getExpression().accept(this,p); } @Override public @Nullable java.util.List<JmlStatementExpr> visitUnary(UnaryTree node, Void p) { JCUnary that = (JCUnary)node; JCExpression arg = that.arg; JCTree.Tag op = that.getTag(); @Nullable java.util.List<JmlStatementExpr> earg = arg.accept(this, p); if (op == JCTree.Tag.NEG) { JCExpression e = treeutils.makeBinary(arg.pos,JCTree.Tag.NE,treeutils.intneqSymbol,sub.copy(arg),treeutils.makeIntLiteral(arg.pos,Integer.MIN_VALUE)).setType(syms.booleanType); JmlStatementExpr a = treeutils.makeAssert(that, Label.ARITHMETIC_OP_RANGE, e); earg = combine( earg, a); } return earg; } @Override public @Nullable java.util.List<JmlStatementExpr> visitVariable(VariableTree node, Void p) { // TODO Auto-generated method stub // Model fields? nonnull vcheck? return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlBinary(JmlBinary that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlBlock(JmlBlock that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlLblExpression(JmlLblExpression that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlMethodInvocation(JmlMethodInvocation that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlPrimitiveTypeTree( JmlPrimitiveTypeTree that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlQuantifiedExpr(JmlQuantifiedExpr that, Void p) { ListBuffer<JCVariableDecl> newdecls = new ListBuffer<JCVariableDecl>(); try { for (JCVariableDecl decl: that.decls) { JCVariableDecl nd = treeutils.makeVarDef(decl.type,decl.name,decl.sym.owner,decl.pos); newdecls.add(nd); JCExpression was = replacements.put(decl.sym, treeutils.makeIdent(decl.pos, nd.sym)); if (was != null) throw new RuntimeException("Same Decl in replacement map"); } @Nullable java.util.List<JmlStatementExpr> range = that.range == null ? null : that.range.accept(this,p); @Nullable java.util.List<JmlStatementExpr> value = that.value.accept(this,p); if (that.range != null) value = prepend(sub.copy(that.range), value); if (range != null) for (JmlStatementExpr a: range) { // FIXME - all the quantified expressions are using the same decls with the same symbols JmlQuantifiedExpr q = M.at(that.pos).JmlQuantifiedExpr(that.op, newdecls.toList(), null, a.expression); q.setType(syms.booleanType); a.expression = q; } if (value != null) for (JmlStatementExpr a: value) { // FIXME - all the quantified expressions are using the same decls with the same symbols JmlQuantifiedExpr q = M.at(that.pos).JmlQuantifiedExpr(that.op, newdecls.toList(), that.range == null ? null : sub.copy(that.range), a.expression); q.setType(syms.booleanType); a.expression = q; } return combine(range, value); } finally { for (JCVariableDecl decl: that.decls) { replacements.remove(decl.sym); } } } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlSetComprehension(JmlSetComprehension that, Void p) { // TODO Auto-generated method stub return null; } @Override public @Nullable java.util.List<JmlStatementExpr> visitJmlSingleton(JmlSingleton that, Void p) { return null; } } }