/*
* 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.lang.model.type.TypeMirror;
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.JmlLabeledStatement;
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.Attribute.Compound;
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.JCLambda;
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;
/** The receiver expression, such as when performing a method call within the body of the method being translated.
*/
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 Map<Name,Integer> oldHeapValues = new HashMap<>();
public Map<Name,Map<Symbol,MethodSymbol>> oldHeapMethods = new HashMap<>();
{
oldHeapMethods.put(null, new HashMap<Symbol,MethodSymbol>());
}
public boolean useNamesForHeap = true; // if false, use arguments for heap
/** (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.oldHeapValues.put(names.fromString(""), this.heapCount);
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;
initialize2(0L);
{ // 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.JmlLabeledStatement(preLabel.name, null, 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,methodDecl.pos);
d.init = 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,classDecl.sym,
treeutils.makeIntLiteral(0, 0));
heapSym = d.sym;
initialStatements.add(d);
}
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, enclosingClass.isEnum() ? 0 : isConstructor ? ++allocCounter : 0));
addStat(treeutils.makeAssume(methodDecl, Label.IMPLICIT_ASSUME, fa ));
// FIXME - the above setting for enums very likely has to be fixed.
}
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);
}
if (esc && isConstructor && !callingThis) {
boolean pv = checkAccessEnabled;
checkAccessEnabled = false; // Not sure about this - all references are to instance fieldsd, no?
try {
addInstanceInitialization();
} finally {
checkAccessEnabled = pv;
}
}
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 {
addInstanceInitializationPass2();
} 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);
String v = JmlOption.value(context,JmlOption.FEASIBILITY);
if (esc && (
Strings.feasibilityContains(Strings.feas_exit,context) ||
Strings.feasibilityContains(Strings.feas_all,context) ||
Strings.feasibilityContains(Strings.feas_debug,context)
)) {
addAssumeCheck(methodDecl,outerFinalizeStats,Strings.atExitAssumeCheckDescription);
}
// 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;
}
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);
if (condition == null) condition = treeutils.trueLit;
}
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 && Strings.feasibilityContains(Strings.feas_debug,context)) {
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 == 1230) Utils.stop();
Name assertname = names.fromString(assertID);
JavaFileObject dsource = log.currentSourceFile();
JCVariableDecl assertDecl = treeutils.makeVarDef(syms.booleanType,assertname,methodDecl == null? (classDecl == null ? 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
&& Strings.feasibilityContains(Strings.feas_debug,context)) {
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_FIELD,
e,
null,null); // FIXME - no associated position?
else addAssert(pos,Label.POSSIBLY_NULL_FIELD,
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_FIELD,
e,
null,null); // FIXME - no associated position?
else addAssert(pos,Label.POSSIBLY_NULL_FIELD,
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 = pv;
}
}
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
Type basetype = clsym.type;
// We use classDecl.type here in case we are in a derived class with a represents clause;
// clsym is the static class of the receiver, which may not have derived represents clauses
if (classDecl != null) {
if (classDecl.type.tsym.isSubClass(clsym,types)) basetype = classDecl.type;
}
try {
if (rac) {
Name mmName = names.fromString(Strings.modelFieldMethodPrefix + varsym.toString());
java.util.List<Type> p = parents(basetype, 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(basetype, 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 (!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._alloc__ <= 0
//addTraceableComment(e2);
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;
}
protected boolean addNullnessTypeConditionId(JCExpression id, DiagnosticPosition pos, Symbol sym, boolean isNonNull, boolean instanceBeingConstructed) {
int p = pos.getPreferredPosition();
boolean nnull = true;
if (!jmltypes.isJmlType(sym.type) && !sym.type.isPrimitive()) {
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 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, type, and allocation 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 = treeutils.trueLit;
for (JmlMethodClause clause : scase.clauses) {
switch (clause.token) {
case OLD:
for (JCVariableDecl decl : ((JmlMethodClauseDecl)clause).decls) {
addTraceableComment(decl,clause.toString());
Name name = names.fromString(decl.name.toString() + "__OLD_" + decl.pos);
JCVariableDecl newdecl = treeutils.makeVarDef(decl.type, name, decl.sym.owner, decl.pos);
newdecl.init = treeutils.makeZeroEquivalentLit(decl.init.pos, decl.init.type);
JCExpression init =
convertJML(treeutils.isTrueLit(preexpr) ? decl.init : treeutils.makeConditional(pos, preexpr, decl.init, treeutils.makeZeroEquivalentLit(pos, decl.init.type)));
addStat(initialStats, newdecl);
JCIdent id = treeutils.makeIdent(clause.pos, newdecl.sym);
paramActuals.put(decl.sym,id);
preparams.put(decl.sym,newdecl);
JCExpressionStatement stat = treeutils.makeAssignStat(init.pos,id,init);
addStat(stat);
exprBiMap.put(id, convertExpr(id));
}
break;
case REQUIRES:
JCExpression ex = ((JmlMethodClauseExpr)clause).expression;
preexpr = treeutils.makeAndSimp(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.JmlLabeledStatement(null, 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;
if (!sy.isStatic() && currentThisExpr == null) continue; // FIXME - JML static?
addNullnessAndTypeConditionsForField(csym, (VarSymbol)sy, beingConstructed);
// FIXME - why both of these?
addNullnessAllocationTypeCondition(methodDecl, sy, beingConstructed && !utils.isJMLStatic(sy));
}
}
protected void addNullnessAndTypeConditionsForField(TypeSymbol csym, VarSymbol sy, boolean beingConstructed) {
// For Java fields
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));
return;
}
}
}
}
// FIXME - should have a DiagnosticPosition for the actual declaration
// FIXME - when is this actually called
// // 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) {
if (methodDecl.name.toString().equals("tick")) Utils.stop();
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 {
ensuresStats.prepend(comment(methodDecl.pos(),"Checking normal postconditions",null));
exsuresStats.prepend(comment(methodDecl.pos(),"Checking exceptional postconditions",null));
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);
addStat( comment(methodDecl,"Class fields for constructor",null));
// First pass sets everything to zero-equivalent
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));
}
}
protected void addInstanceInitializationPass2() {
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,"Initializing Class fields for constructors",null));
// Second pass computes the initialized result
for (JCTree t: classDecl.defs) {
if (t instanceof JCVariableDecl) {
JCVariableDecl vd = (JCVariableDecl)t;
if (vd.init == null) continue;
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 e = convertExpr(vd.init);
e = addImplicitConversion(e, vd.type, e);
addStat(treeutils.makeAssignStat(vd.pos, receiver, e));
} else if (t instanceof JCBlock) {
if ( ((JCBlock)t).isStatic() ) continue;
convert(t);
}
}
}
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>>();
protected
Map<Name,JmlLabeledStatement> labelStatements = new HashMap<Name,JmlLabeledStatement>();
//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.
JmlLabeledStatement stat = M.at(that).JmlLabeledStatement(that.label, null, 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);
labelStatements.put(that.label, stat);
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); // Remove the statement because we are leaving the scope of the label
}
}
@Override
public void visitLambda(JCLambda that) {
notImplemented(that,"cannot translate methods containing lambdas, yet");
// FIXME - do something with lambdas
result = eresult = 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
}
protected Name closeName;
// This translation follows https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-ResourceList
protected void transformTryWithResources(JCTry that) {
if (that.resources == null || that.resources.isEmpty()) return;
if (closeName == null) closeName = names.fromString("close");
JCTree resource = that.resources.head;
List<JCTree> resourceRest = that.resources.tail;
int pos = resource.pos;
JCVariableDecl decl = (JCVariableDecl)resource;
decl.mods.flags |= Flags.FINAL; // implicitly final
ListBuffer<JCStatement> stats = new ListBuffer<>();
stats.add((JCStatement)resource);
Name throwableName = names.fromString("__JMLthrowableException_" + resource.pos);
JCVariableDecl throwableDecl = treeutils.makeVarDef(syms.throwableType, throwableName, methodDecl != null ? methodDecl.sym : classDecl.sym, resource.pos);
Type atype = attr.nullableAnnotationSymbol.type;
throwableDecl.mods.annotations = throwableDecl.mods.annotations.append(M.Annotation(M.Type(atype),List.<JCExpression>nil()));
List<com.sun.tools.javac.util.Pair<Symbol.MethodSymbol,Attribute>>nlist = List.<com.sun.tools.javac.util.Pair<MethodSymbol,Attribute>>nil();
Compound c = new Attribute.Compound(atype,nlist);
throwableDecl.sym.appendAttributes(List.<Compound>of(c));
throwableDecl.init = treeutils.nullLit;
stats.add(throwableDecl);
Name catchName = names.fromString("__JMLthrowableCatch_" + resource.pos);
JCVariableDecl catchDecl = treeutils.makeVarDef(syms.throwableType, catchName, methodDecl != null ? methodDecl.sym : classDecl.sym, resource.pos);
JCExpression exAssign = M.Assign(treeutils.makeIdent(pos,throwableDecl.sym), treeutils.makeIdent(pos,catchDecl.sym));
exAssign.pos = pos;
JCStatement exStat = M.at(pos).Exec(exAssign);
JCStatement exThrow = M.Throw(treeutils.makeIdent(pos, throwableDecl.sym));
exThrow.pos = pos;
JCCatch newCatch = M.at(pos).Catch(catchDecl, M.at(pos).Block(0L, List.<JCStatement>of(exStat,exThrow)));
JCIdent id = treeutils.makeIdent(pos,decl.sym);
MethodSymbol msym = null;
for (Symbol sym: ((ClassSymbol)resource.type.tsym).members().getElementsByName(closeName)) {
if (sym instanceof MethodSymbol) {
MethodSymbol m = (MethodSymbol)sym;
if (m.getParameters().isEmpty()) {
msym = m;
break;
}
}
}
M.at(pos);
JCExpression fcn1 = M.Select(id, msym);
fcn1.type = msym.type;
JCMethodInvocation closeCallExpr1 = M.Apply(List.<JCExpression>nil(),fcn1,List.<JCExpression>nil());
closeCallExpr1.type = syms.voidType;
JCExpressionStatement closeCall1 = M.Exec(closeCallExpr1);
JCExpression fcn2 = M.Select(id, msym);
fcn2.type = msym.type;
JCMethodInvocation closeCallExpr2 = M.Apply(List.<JCExpression>nil(),fcn2,List.<JCExpression>nil());
closeCallExpr2.type = syms.voidType;
JCExpressionStatement closeCall2 = M.Exec(closeCallExpr2);
Name closeCatchName = names.fromString("__JMLcloseCatch_" + resource.pos);
JCVariableDecl closeCatchDecl = treeutils.makeVarDef(syms.throwableType, closeCatchName, esc ? null: methodDecl != null ? methodDecl.sym : classDecl.sym, resource.pos);
JCCatch resourceCloseCatch = M.at(pos).Catch(closeCatchDecl, M.at(pos).Block(0L, List.<JCStatement>nil())); // FIXME - should save the suppressed exception
JCTry closetry = M.at(pos).Try(List.<JCTree>nil(), M.at(pos).Block(0L, List.<JCStatement>of(closeCall1)), List.<JCCatch>of(resourceCloseCatch), null);
JCExpression comp = treeutils.makeNotNull(pos, treeutils.makeIdent(pos, throwableDecl.sym));
JCStatement thenpart = M.at(pos).Block(0L, List.<JCStatement>of(M.If(comp,
M.at(pos).Block(0L, List.<JCStatement>of(closetry)),
closeCall2).setType(syms.booleanType)));
comp = treeutils.makeNotNull(pos, treeutils.makeIdent(pos, decl.sym));
JCBlock finalBlock = M.at(pos).Block(0L,
List.<JCStatement>of(
M.at(pos).If(comp, thenpart, null).setType(syms.booleanType)));
JCTry newtry = M.at(pos).Try(resourceRest, that.body, List.<JCCatch>of(newCatch), finalBlock);
stats.add(newtry);
that.resources = List.<JCTree>nil();
that.body = M.at(pos).Block(0L, stats.toList());
if (that.catchers.isEmpty() && that.finalizer == null) that.finalizer = M.at(pos).Block(0L, List.<JCStatement>nil());
// FIXME - if the original try does not have catchers or finally, it can be converted to just a block
// FIXME - what about finallyCanCompleteNormally in all of the above
// FIXME - add position, types, symbols
}
// OK
// FIXME - review and cleanup for both esc and rac
@Override
public void visitTry(JCTry that) {
if (that.resources != null && !that.resources.isEmpty()) transformTryWithResources(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);
Type ct = catcher.param.type;
JCExpression e = treeutils.falseLit;
if (ct.isUnion()) {
Type.UnionClassType uct = ((Type.UnionClassType)ct);
for (TypeMirror t: uct.getAlternatives()) {
Type tt = (Type)t;
e = treeutils.makeOrSimp(catcher.pos, e, treeutils.makeInstanceOf(catcher.param.pos,id,tt));
}
} else {
e = treeutils.makeInstanceOf(catcher.param.pos,id,ct);
}
addAssume(catcher.pos(),Label.IMPLICIT_ASSUME,e);
addRecInvariants(true,catcher.param,id); // This only adds invariants for the union type
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);
// These assignments must be here (and not in BasicBlocker...) because they are needed by RAC
// 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);
JCExpression isLocal = treeutils.falseLit;
if (methodDecl.sym.isConstructor() && pstoreref instanceof JCFieldAccess) {
JCFieldAccess faa = (JCFieldAccess)pstoreref;
if (!faa.sym.isStatic() && fa.sym.owner == methodDecl.sym.owner) {
// FIXME - do we need to convertJML on the thisid?
isLocal = treeutils.makeEqObject(faa.pos, convertJML(faa.selected), makeThisId(classDecl.pos,classDecl.sym));
}
}
// if (methodDecl.sym.isConstructor() ) {
// JCExpression idthis = treeutils.makeOld(pos, baseThisExpr);
// if (rac) idthis = convertJML(idthis);
// isLocal = treeutils.makeEqObject(posp, idthis,
// convertJML(fa.selected));
// }
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 isLocal;
} else if (pfac instanceof JCIdent) {
JCIdent pid = (JCIdent)pfac;
if (!isContainedIn(fa.sym,pid.sym)) return isLocal;
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 treeutils.makeOrSimp(posp, isLocal, result);
} else if (pfac instanceof JCFieldAccess) {
JCFieldAccess pfa = (JCFieldAccess)pfac;
if (pfa.name == null) {
JCExpression or = isLocal;
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 isLocal;
}
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 treeutils.makeOrSimp(posp, isLocal, 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;
/*@ nullable */ JmlMethodSpecs mspecs = specs.getDenestedSpecs(methodDecl.sym);
// mspecs can be null if we are translating a initializer block
if (mspecs != null) for (JmlSpecificationCase c: mspecs.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) {
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) {
if (scannedItem.toString().equals("n")) Utils.stop();
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;
JCExpression isLocal = treeutils.falseLit;
if (methodDecl.sym.isConstructor() && storeref instanceof JCFieldAccess) {
JCFieldAccess fa = (JCFieldAccess)storeref;
// FIXME - do we need to convertJML on the thisid?
if (!fa.sym.isStatic() && fa.sym.owner == methodDecl.sym.owner) {
isLocal = treeutils.makeEqObject(fa.pos, convertJML(fa.selected), makeThisId(classDecl.pos,classDecl.sym));
}
}
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) {
// FIXME - this branch looks wrong
pstorerefs = ((JmlMethodClauseStoreRef)anyAssignableClause).list;
if (token == JmlTokenKind.ACCESSIBLE) pstorerefs = List.<JCExpression>of(M.JmlStoreRefKeyword(JmlTokenKind.BSEVERYTHING));
} else if (methodDecl.sym.isConstructor() && token == JmlTokenKind.ASSIGNABLE) {
pstorerefs = List.<JCExpression>of(M.JmlStoreRefKeyword(JmlTokenKind.BSNOTHING));
} 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
}
}
asg = treeutils.makeOrSimp(asg.pos, asg, isLocal);
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;
if (enclosingMethod == null) return treeutils.trueLit; // initializer block
if (enclosingMethod.isConstructor() && obj instanceof JCIdent && ((JCIdent)obj).sym.toString().equals(Strings.thisName) ) return treeutils.trueLit; // Fields of an object being constructed are always assignable
// 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> thisStarStoreRefs(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());
MethodSymbol msym = null;
if (pos instanceof JCMethodInvocation) {
msym = (MethodSymbol)treeutils.getSym(((JCMethodInvocation)pos).meth);
} else if (pos instanceof JmlMethodDecl) {
msym = ((JmlMethodDecl)pos).sym;
}
if (msym != null) attr.addHelper(msym);
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;
protected JCExpression allocCounterLE(DiagnosticPosition pos, JCExpression expr, int counter) {
JCFieldAccess fa = treeutils.makeSelect(pos.getPreferredPosition(), expr, allocSym);
JCExpression e = treeutils.makeBinary(pos, JCTree.Tag.LE, treeutils.intleSymbol, fa, treeutils.makeIntLiteral(pos, counter));
return treeutils.makeOr(pos.getPreferredPosition(), treeutils.makeEqNull(pos.getPreferredPosition(), expr),e);
}
protected JCExpression allocCounterEQ(DiagnosticPosition pos, JCExpression expr, int counter) {
JCFieldAccess fa = treeutils.makeSelect(pos.getPreferredPosition(), expr, allocSym);
JCExpression e = treeutils.makeBinary(pos, JCTree.Tag.EQ, treeutils.inteqSymbol, fa, treeutils.makeIntLiteral(pos, counter));
return treeutils.makeOr(pos.getPreferredPosition(), treeutils.makeEqNull(pos.getPreferredPosition(), expr),e);
}
protected JCExpression allocCounterGT(DiagnosticPosition pos, JCExpression expr, int counter) {
JCFieldAccess fa = treeutils.makeSelect(pos.getPreferredPosition(), expr, allocSym);
JCExpression e = treeutils.makeBinary(pos, JCTree.Tag.GT, treeutils.intgtSymbol, fa, treeutils.makeIntLiteral(pos, counter));
return treeutils.makeOr(pos.getPreferredPosition(), treeutils.makeEqNull(pos.getPreferredPosition(), expr),e);
}
/** 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);
if (condition != null) e = treeutils.makeImplies(fa.selected.pos, condition, e);
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) && !useNamesForHeap) {
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 = oldHeapMethods.get(oldenv == null ? null : oldenv.name).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) || trArgs.isEmpty()) {
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;
}
// The following line is used by spec inference to know what the newly declared variable is the
// result of. The : in the comment is essential and used to delineate the descriptive text from the text of the expression
addStat(comment(that,"Declaration for return value: " + that.toString(), methodDecl.source()));
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;
List<JCExpression> convertedArgs = convertCopy(trArgs);
if (!utils.isJMLStatic(calleeMethodSym)) {
convertedArgs = convertedArgs.prepend(newThisExpr);
}
if (!convertedArgs.isEmpty()) e = treeutils.makeMethodInvocation(that,null,s,convertedArgs);
else e = treeutils.makeIdent(that, s);
e = condition == null ? 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);
MethodSymbol newCalleeSym = oldHeapMethods.get(oldenv == null ? null : oldenv.name).get(calleeMethodSym);
if (newCalleeSym == null) {
log.error("jml.internal","No logical function for method " + calleeMethodSym.getQualifiedName());
}
List<JCExpression> convertedArgs = convertCopy(trArgs);
if (!utils.isJMLStatic(calleeMethodSym)) {
convertedArgs = convertedArgs.prepend(newThisExpr);
}
JCExpression methCall = treeutils.makeMethodInvocation(that,null,newCalleeSym,convertedArgs);
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()) {
newAllocation2(that,resultExpr); // FIXME - should have an JCIdent?
JCExpression e = allocCounterEQ(that, resultExpr, ++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);
JmlLabeledStatement stat = M.at(that).JmlLabeledStatement(calllabel,null,bl);
addStat(stat);
preLabel = M.at(that).Ident(calllabel);
labelOldLists.put(calllabel, currentStatements);
oldStatements = currentStatements;
defaultOldLabel = calllabel;
labelStatements.put(calllabel, stat);
stat.extraStatements.addAll(currentStatements);
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) {
if (applyNesting <= 1 && !(isHelper(calleeMethodSym) && meth instanceof JCIdent)) {
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
}
}
clearInvariants();
// 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) + ")");
clearInvariants();
}
}
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;
ListBuffer<JCStatement> oldStatements = currentStatements;
if (rac) {
preId = newTemp(treeutils.falseLit);
pushBlock();
}
JCExpression pre = convertCopy(translatingJML ? savedCondition : treeutils.trueLit);
JCExpression prex = treeutils.trueLit;
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
for (JCVariableDecl decl : ((JmlMethodClauseDecl)clause).decls) {
if (paramActuals.get(decl.sym) == null) {
addTraceableComment(decl,clause.toString());
Name name = names.fromString(decl.name.toString() + "__OLD_" + decl.pos);
JCVariableDecl newdecl = treeutils.makeVarDef(decl.type, name, methodDecl.sym, decl.pos);
newdecl.init = treeutils.makeZeroEquivalentLit(decl.init.pos, decl.init.type);
JCExpression init =
convertJML(treeutils.isTrueLit(prex) ? decl.init : treeutils.makeConditional(decl.pos, prex, decl.init, treeutils.makeZeroEquivalentLit(decl.pos, decl.init.type)));
addStat(oldStatements,newdecl);
JCIdent id = treeutils.makeIdent(clause.pos, newdecl.sym);
paramActuals.put(decl.sym,id);
JCExpressionStatement stat = treeutils.makeAssignStat(init.pos,id,init);
addStat(stat);
exprBiMap.put(id, convertExpr(id));
}
}
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 = treeutils.makeAndSimp(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) {
// FIXME - ignore if after all requires?
// FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions
insertDeclarationsForOld(null, (JmlMethodClauseDecl)clause);
} 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) {
if (checkAccessEnabled) {
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);
// }
checkAgainstCallerSpecs(JmlTokenKind.ASSIGNABLE, that, M.at(cs).JmlStoreRefKeyword(JmlTokenKind.BSNOTHING),pre, savedThisId, newThisId, cs.source());
} else {
if (isPure(mpsym)) {
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 && checkAccessEnabled) {
// If there are no accessible clauses in the spec case, use a default
// 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);
}
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: {
// FIXME - ignore if after all requires?
// FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions
insertDeclarationsForOld(pre,(JmlMethodClauseDecl)clause);
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>();
boolean containsEverything = false;
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);
if (location instanceof JmlStoreRefKeyword && ((JmlStoreRefKeyword)location).token == JmlTokenKind.BSEVERYTHING) {
containsEverything = true;
}
}
}
JCStatement havoc = M.at(clause.pos).JmlHavocStatement(newlist.toList());
addStat(havoc);
if (containsEverything) {
addNullnessAndTypeConditionsForInheritedFields(classDecl.sym, false);
}
}
break;
default:
// skip everything else
break;
}
} catch (JmlNotImplementedException e) {
notImplemented(clause.token.internedName() + " clause containing ",e, clause.source());
}
}
if (useDefault && !translatingJML) {
if (isThisCall) {
// default for a 'this' call is all local fields
ListBuffer<JCExpression> fields = new ListBuffer<>();
for (JCTree d: classDecl.defs) {
if (!(d instanceof JCVariableDecl)) continue;
JCExpression e = treeutils.makeIdent(d.pos,((JCVariableDecl)d).sym);
fields.add(e);
}
JCStatement havoc = M.at(cs.pos).JmlHavocStatement(fields.toList());
addStat(havoc);
} else if (newclass == null && !isPure) {
// 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);
addNullnessAndTypeConditionsForInheritedFields(classDecl.sym, false);
} else {
// default for constructor call is \nothing -- should it be \everything for binary classes
JCStatement havoc = M.at(cs.pos).JmlHavocStatement(List.<JCExpression>of(M.at(cs.pos).JmlStoreRefKeyword(JmlTokenKind.BSNOTHING)));
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);
addAssume(that,Label.IMPLICIT_ASSUME, allocCounterLE(that.pos(), resultId, ++allocCounter));
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?
currentStatements = ensuresStats;
for (JmlMethodClause clause : cs.clauses) {
JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile;
JavaFileObject prevSource = null;
try {
switch (clause.token) {
case OLD:
{
JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause;
// FIXME - ignore if after all requires?
// FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions
insertDeclarationsForOld(null,olddecl);
// 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;
default:
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());
}
}
for (JmlMethodClause clause : cs.clauses) {
JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile;
JavaFileObject prevSource = null;
try {
switch (clause.token) {
case OLD: break;
case FORALL: 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 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());
}
}
currentStatements = exsuresStats;
for (JmlMethodClause clause : cs.clauses) {
JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile;
JavaFileObject prevSource = null;
try {
switch (clause.token) {
case OLD:
{
JmlMethodClauseDecl olddecl = (JmlMethodClauseDecl)clause;
// FIXME - ignore if after all requires?
// FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions
insertDeclarationsForOld(null,olddecl);
break;
}
case FORALL:
break;
default:
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());
}
}
for (JmlMethodClause clause : cs.clauses) {
JavaFileObject clauseSource = clause.sourcefile == null ? log.currentSourceFile() : clause.sourcefile;
JavaFileObject prevSource = null;
try {
switch (clause.token) {
case OLD: break;
case FORALL: break;
case ENSURES: break;
case SIGNALS:
// FIXME - review this
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:
{
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, JCExpression resultExpr) {
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(resultExpr), isAllocSym);
JCStatement st = treeutils.makeAssignStat(p, fa, treeutils.makeBooleanLiteral(p,true));
addStat( st );
}
protected void insertDeclarationsForOld(JCExpression precondition, JmlMethodClauseDecl clause) {
// // FIXME - ignore if after all requires?
// // FIXME - needs to be in a block, but don't like to have assignments to compute the preconditions
// List<JCVariableDecl> decls = clause.decls;
// for (JCVariableDecl d: decls) {
// // Need a new symbol
// int pos = d.pos;
// Name name = names.fromString(d.name.toString() + "__OLD__");
// JCVariableDecl newdef = treeutils.makeVarDef(d.type, name, methodDecl.sym, d.pos);
// JCIdent id = treeutils.makeIdent(d.pos, newdef.sym);
// paramActuals.put(d.sym, id);
// newdef.init = convertJML(precondition == null ? d.init : treeutils.makeConditional(pos, precondition, d.init, treeutils.makeZeroEquivalentLit(pos, d.init.type)));
// addStat(newdef);
// }
}
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) {
JCExpression e = allocCounterEQ(p, convertCopy(array), ++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" : "";
;
Name id = names.fromString(methodName);
MethodSymbol msym = getMethod(expr.type,id);
JCFieldAccess receiver = M.at(expr).Select(expr, id);
receiver.type = msym.type;
receiver.sym = msym;
JCMethodInvocation call = M.at(expr).Apply(List.<JCExpression>nil(), receiver, List.<JCExpression>nil());
call.setType(unboxed);
return convertExpr(call);
}
protected MethodSymbol getMethod(Type type, Name nm) {
Iterator<Symbol> iter = type.tsym.members().getElementsByName(nm).iterator();
if (iter.hasNext()) return (MethodSymbol)iter.next();
return null;
}
protected JCExpression createBoxingStatsAndExpr(JCExpression expr, Type newtype) {
TypeTag tag = expr.type.getTag();
newtype = boxedType(expr.type);
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" : "";
;
Name id = names.fromString(methodName);
MethodSymbol msym = getMethod(newtype,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);
addNullnessTypeConditionId(tmp, expr, tmp.sym, true, false);
JCFieldAccess receiver = M.at(expr).Select(tmp, id);
receiver.type = msym.type;
receiver.sym = msym;
JCMethodInvocation call = M.at(expr).Apply(List.<JCExpression>nil(), receiver, List.<JCExpression>nil());
call.setType(expr.type); // Note: no symbol set, OK since this is esc
JCExpression e = treeutils.makeEquality(expr.pos,call,expr);
addAssume(expr,Label.IMPLICIT_ASSUME,convertExpr(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)) {
selected = trexpr;
boolean var = false;
// FIXME - why must selected be a JCIdent here and below
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;
}
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;
}
if (utils.rac && inOldEnv && utils.isExprLocal(that.sym.flags())) {
String message = "quantifier variable inside a \\old or \\pre expression: " + that.toString();
throw new JmlNotImplementedException(that,message);
}
// if (utils.rac && that.sym.owner instanceof MethodSymbol) {
// if (that.sym.owner != methodDecl.sym) {
// that.sym.owner = methodDecl.sym;
// }
// }
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)) {
// currentThisExpr is null if the symbol is static
TypeSymbol tsym = currentThisExpr != null ? currentThisExpr.type.tsym : (TypeSymbol)sym.owner; // FIXME - perhaps can always be sym.owner
addRepresentsAxioms(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 & Utils.JMLADDED) != 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);
JCIdent id = newTemp(value);
// FIXME - fix the nullness determination in the following line
if (!rac) addNullnessTypeConditionId(id, that.var, id.sym, false, false);
value = addImplicitConversion(array, that.var.type, id);
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).JmlLabeledStatement(label,null,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);
changeState();
}
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);
}
@Override
public void visitJmlLabeledStatement(JmlLabeledStatement that) {
visitLabelled(that);
}
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 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.name);
JmlLabeledStatement labelStat = labelStatements.get(label.name);
labelStat.extraStatements.add(stat);
if (list != null) {
list.add(stat);
} else {
ListBuffer<JCStatement> stlist = labelOldLists.get(label.name);
ListBuffer<JCStatement> newlist = new ListBuffer<JCStatement>();
for (JCStatement st: stlist) {
if (st instanceof JCLabeledStatement && ((JCLabeledStatement)st).label.equals(label.name)) {
newlist.add(stat);
}
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.javaType = that.javaType;
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);
JmlLabeledStatement labelStat = labelStatements.get(label);
labelStat.extraStatements.add(v);
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);
JmlLabeledStatement labelStat = labelStatements.get(label);
labelStat.extraStatements.add(v);
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);
JCExpression exprCopy = convertCopy(arg);
if (assumingPostConditions) {
// allocCounter is already bumped up earlier when it was declared that the result was allocated
e = allocCounterEQ(that, exprCopy, allocCounter);
} else {
// FIXME - explain why this is different than the above - this would be the postcondition for the method
e = allocCounterGT(that, exprCopy, 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 && rac) {
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).JmlLabeledStatement(label,null,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).JmlLabeledStatement(label,null,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).JmlLabeledStatement(label,null,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);
changeState();
}
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);
}
if (esc) changeState(); // loop is different state than break block
// 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);
this.oldHeapValues.put(label, this.heapCount);
{
Map<Symbol,MethodSymbol> pm = oldHeapMethods.get(null);
Map<Symbol,MethodSymbol> newpm = new HashMap<Symbol,MethodSymbol>(pm);
oldHeapMethods.put(label, newpm);
}
// FIXME - this requires all labels to be unique
}
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 && !useNamesForHeap) {
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
int hc = heapCount;
if (oldenv != null) hc = oldHeapValues.get(oldenv.name);
Name newMethodNameWithHeap = names.fromString(newMethodName.toString() + ((useNamesForHeap && !isFunction) ? ("H" + hc) : ""));
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, newMethodNameWithHeap, msym.getReturnType(), (TypeSymbol)methodDecl.sym.owner, newParamTypes);
//pureMethod.put(msym,newsym);
{
Map<Symbol,MethodSymbol> pm;
Name nm = oldenv == null ? null : oldenv.name;
pm = oldHeapMethods.get(nm);
if (pm == null) oldHeapMethods.put(nm, pm = new HashMap<Symbol,MethodSymbol>());
pm.put(msym, newsym);
}
// Construct axioms for the type of the result
if (msym.getReturnType().isPrimitiveOrVoid()) {
// FIXME - add any range restrictions for primtiive types
} else {
JCExpression fcn = treeutils.makeIdent(Position.NOPOS,newsym);
JCMethodInvocation call = M.at(Position.NOPOS).Apply(
List.<JCExpression>nil(), fcn, newParamsWithHeap);
call.type = newsym.getReturnType();
JavaFileObject clauseSource = log.currentSourceFile();
DiagnosticPosition dpos = callLocation;
JCExpression e = M.at(dpos).TypeTest(call,M.Type(msym.getReturnType()));
e = treeutils.makeOr(dpos.getPreferredPosition(),treeutils.makeEqNull(dpos.getPreferredPosition(), call), e);
if (newDeclsList.isEmpty()) {
// e is just 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
treeutils.trueLit,
e);
e.type = syms.booleanType;
}
currentStatements = savedForAxioms;
addAssume(dpos,Label.IMPLICIT_ASSUME,e,dpos,clauseSource);
}
// 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__" + newMethodNameWithHeap.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,useNamesForHeap ? Label.METHOD_DEFINITION : 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> visitJmlLabeledStatement(JmlLabeledStatement that, Void p) {
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;
}
}
}