/* * This file is part of the OpenJML project. * Author: David R. Cok */ package com.sun.tools.javac.comp; import javax.tools.JavaFileObject; import org.jmlspecs.openjml.IJmlVisitor; import org.jmlspecs.openjml.JmlSpecs; import org.jmlspecs.openjml.JmlTree.*; import org.jmlspecs.openjml.Utils; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; /** * This extends Flow to add flow checks to specifications. Flow checks for:<BR> * uninitialized variables<BR> * assignments to final variables<BR> * unreachable statements<BR> * forward references<BR> * fall-through in cases<BR> * improper use of checked exceptions<BR> * exception never thrown in try<BR> * redundant cast <BR> * The JML additions just make sure that ghost fields, model methods and model classes * are visited by the flow tree walker. * * @author David Cok */ // FIXME - I think we need to implement the JML visit methods in order to do checks within JML expressions? // FIXME - these might apply to JML clauses: redundant cast, forward references, uninit variables public class JmlFlow extends Flow { /** Registers a singleton factory for JmlFlow against the flowKey; the factory * produces a singleton (per context) instance of JmlFlow * @param context the context in which to register the instance */ public static void preRegister(final Context context) { context.put(Flow.flowKey, new Context.Factory<Flow>() { Flow instance = null; public Flow make(Context context) { if (instance == null) instance = new JmlFlow(context); return instance; } }); } /** The compilation context of this instance */ protected Context context; /** A constructor, but use instance() to generate new instances of this class. */ protected JmlFlow(Context context) { super(context); this.context = context; } class JmlAliveAnalyzer extends AliveAnalyzer implements IJmlVisitor { /** This is a stack of the declarations occurring in JML quantifier expressions, * possible nested (and therefore stacked). */ protected java.util.List<List<JCVariableDecl>> quantDeclStack = new java.util.LinkedList<List<JCVariableDecl>>(); //// These are implemented /** This is overridden in order to handle JML method call-like features (e.g. \typeof) */ @Override public void visitApply(JCMethodInvocation tree) { super.visitApply(tree); } @Override public void visitJmlMethodDecl(JmlMethodDecl that) { JavaFileObject prev = Log.instance(context).useSource(that.sourcefile); try { visitMethodDef(that); } finally { Log.instance(context).useSource(prev); } } @Override public void visitJmlMethodInvocation(JmlMethodInvocation that) { visitApply(that); } @Override public void visitJmlBinary(JmlBinary that) { scan(that.lhs); scan(that.rhs); } @Override public void visitJmlBlock(JmlBlock that) { scan(that.stats); } @Override public void visitJmlChoose(JmlChoose that) { scan(that.orBlocks); scan(that.elseBlock); } @Override public void visitJmlClassDecl(JmlClassDecl that) { visitClassDef(that); } @Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { visitTopLevel(that); } @Override public void visitJmlDoWhileLoop(JmlDoWhileLoop that) { visitDoLoop(that); } @Override public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) { visitForeachLoop(that); } @Override public void visitJmlForLoop(JmlForLoop that) { visitForLoop(that); } @Override public void visitJmlImport(JmlImport that) { visitImport(that); } @Override public void visitJmlSingleton(JmlSingleton that) { // nothing to do } @Override public void visitJmlLabeledStatement(JmlLabeledStatement that) { // scan(that.extraStatements.toList()); scan(that.body); } @Override public void visitJmlLblExpression(JmlLblExpression that) { scan(that.expression); } @Override public void visitJmlStatement(JmlStatement that) { scan(that.statement); } @Override public void visitJmlStatementDecls(JmlStatementDecls that) { for (JCStatement s: that.list) { scan(s); } } @Override public void visitJmlStatementExpr(JmlStatementExpr that) { // nothing to do for this checker } @Override public void visitJmlStatementHavoc(JmlStatementHavoc that) { scan(that.storerefs); } @Override public void visitJmlVariableDecl(JmlVariableDecl that) { visitVarDef(that); } @Override public void visitJmlWhileLoop(JmlWhileLoop that) { visitWhileLoop(that); } @Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { // No action to take } @Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) { // FIXME - check array dimensions? } @Override public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) { // nothing to do for this checker } @Override public void visitIdent(JCIdent that) { for (List<JCVariableDecl> list: quantDeclStack) { for (JCVariableDecl decl: list) { if (decl.sym.equals(that.sym)) return; } } super.visitIdent(that); } @Override public void visitJmlSetComprehension(JmlSetComprehension that) { // FIXME: Skipping set comprehension } @Override public void visitJmlStatementSpec(JmlStatementSpec that) { // Is called, but nothing to check } @Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { // FIXME: skipping store-ref expressions // we could call scanExpr on each expression, but we have to watch for special expressions } //// These are not implemented @Override public void visitJmlGroupName(JmlGroupName that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlGroupNameg"); } @Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable tree) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseCallable"); } @Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseConditional"); } @Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseDecl"); } @Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseExpr"); } @Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseGroup"); } @Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSigOnly"); } @Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSignals"); } @Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseStoreRef"); } @Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodSpecs"); } @Override public void visitJmlSpecificationCase(JmlSpecificationCase that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlSpecificationCase"); } @Override public void visitJmlStatementLoop(JmlStatementLoop that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStatementLoop"); } @Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) { // scan(that.expression); // scan(that.lo); // scan(that.hi); Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStoreRefArrayRange"); } @Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConditional"); } @Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConstraint"); } @Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseDecl"); } @Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseExpr"); } @Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseIn"); } @Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseInitializer"); } @Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMaps"); } @Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMonitorsFor"); } @Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseRepresents"); } public void visitJmlMethodSig(JmlMethodSig that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlConstraintMethodSig"); } public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlModelProgramStatement"); } // Instead of overriding visitClassDef (which does a lot of processing), a hook // (moreClassDef) was added into the middle of its implementation; this hook does // nothing in the base class, but is overridden below to do additional processing. /** Overridden to be sure that ghost/model declarations within the class are * visited by the flow checker. */ @Override public void moreClassDef(JCClassDecl tree) { // Do nothing if the class is not attibuted if (tree.sym == null) return; // Do nothing if the class is already instrumented for RAC if (Utils.instance(context).isInstrumented(tree.mods.flags)) return; // Do nothing if the class has no specs (i.e., is binary) JmlSpecs.TypeSpecs tspecs = JmlSpecs.instance(context).get(tree.sym); if (tspecs == null) return; // All of these are added to the main AST // JavaFileObject prev = Log.instance(context).currentSourceFile(); // try { // // Do Flow processing on each JML clause of the class declaration // for (JmlTypeClauseDecl c : tspecs.decls) { // JCTree d = c.decl; // Log.instance(context).useSource(c.source()); // if (c.modifiers != null && (c.modifiers.flags & Flags.SYNTHETIC) != 0) { // continue; // } // d.accept(this); // } // } finally { // Log.instance(context).useSource(prev); // } } @Override protected boolean resolveJump(JCTree tree, ListBuffer<PendingExit> oldPendingExits, JumpKind jk) { boolean resolved = false; List<PendingExit> exits = pendingExits.toList(); pendingExits = oldPendingExits; for (; exits.nonEmpty(); exits = exits.tail) { PendingExit exit = exits.head; if (exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree) { exit.resolveJump(); resolved = true; } else if(exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree && tree instanceof JmlEnhancedForLoop && // JLS -- adapted to fit JDK8 programming style jk.getTarget(exit.tree) == ((JmlEnhancedForLoop)tree).internalForLoop){ exit.resolveJump(); resolved = true; } else { pendingExits.append(exit); } } return resolved; } /** Resolve all continues of this statement. */ boolean resolveContinues(JCTree tree) { return resolveJump(tree, new ListBuffer<PendingExit>(), JumpKind.CONTINUE); } /** Resolve all breaks of this statement. */ boolean resolveBreaks(JCTree tree, ListBuffer<PendingExit> oldPendingExits) { return resolveJump(tree, oldPendingExits, JumpKind.BREAK); } } class JmlAssignAnalyzer extends AssignAnalyzer implements IJmlVisitor { /** This is a stack of the declarations occurring in JML quantifier expressions, * possible nested (and therefore stacked). */ protected java.util.List<List<JCVariableDecl>> quantDeclStack = new java.util.LinkedList<List<JCVariableDecl>>(); //// These are implemented /** This is overridden in order to handle JML method call-like features (e.g. \typeof) */ @Override public void visitApply(JCMethodInvocation tree) { if (tree.meth == null) return; if (tree.meth.type == null) { // FIXME - need to do this just because we don't have full attribution in trEnhancedForLoop scanExpr(tree.meth); scanExprs(tree.args); } else { super.visitApply(tree); } // Ignore JML functions (FIXME - should we make this a JmlTreeScanner and do lots more checks?) } @Override public void visitJmlMethodDecl(JmlMethodDecl that) { visitMethodDef(that); } @Override public void visitJmlMethodInvocation(JmlMethodInvocation that) { visitApply(that); } @Override public void visitJmlBinary(JmlBinary that) { scan(that.lhs); scan(that.rhs); } @Override public void visitJmlBlock(JmlBlock that) { scan(that.stats); } @Override public void visitJmlChoose(JmlChoose that) { scan(that.orBlocks); scan(that.elseBlock); } @Override public void visitJmlClassDecl(JmlClassDecl that) { visitClassDef(that); } @Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { visitTopLevel(that); } @Override public void visitJmlDoWhileLoop(JmlDoWhileLoop that) { visitDoLoop(that); } @Override public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) { visitForeachLoop(that); } @Override public void visitJmlForLoop(JmlForLoop that) { visitForLoop(that); } @Override public void visitJmlImport(JmlImport that) { visitImport(that); } @Override public void visitJmlSingleton(JmlSingleton that) { // nothing to do } public void visitJmlLabeledStatement(JmlLabeledStatement that) { // scan(that.extraStatements.toList()); scan(that.body); } @Override public void visitJmlLblExpression(JmlLblExpression that) { scan(that.expression); } @Override public void visitJmlStatement(JmlStatement that) { scan(that.statement); } @Override public void visitJmlStatementDecls(JmlStatementDecls that) { for (JCStatement s: that.list) { scan(s); } } @Override public void visitJmlStatementExpr(JmlStatementExpr that) { scanExpr(that.expression); scanExpr(that.optionalExpression); } @Override public void visitJmlStatementHavoc(JmlStatementHavoc that) { scan(that.storerefs); } @Override public void visitJmlVariableDecl(JmlVariableDecl that) { visitVarDef(that); } @Override public void visitJmlWhileLoop(JmlWhileLoop that) { visitWhileLoop(that); } @Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { // No action to take } @Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) { // FIXME - check array dimensions? } @Override public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) { quantDeclStack.add(0,that.decls); if (that.racexpr != null) { scanExpr(that.racexpr); } else { scanExpr(that.range); scanExpr(that.value); } quantDeclStack.remove(0); } @Override public void visitIdent(JCIdent that) { for (List<JCVariableDecl> list: quantDeclStack) { for (JCVariableDecl decl: list) { if (decl.sym.equals(that.sym)) return; } } super.visitIdent(that); } @Override public void visitJmlSetComprehension(JmlSetComprehension that) { // FIXME: Skipping set comprehension } @Override public void visitJmlStatementSpec(JmlStatementSpec that) { // Is called, but nothing to check } @Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { // FIXME: skipping store-ref expressions // we could call scanExpr on each expression, but we have to watch for special expressions } //// These are not implemented @Override public void visitJmlGroupName(JmlGroupName that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlGroupNameg"); } @Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable tree) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseCallable"); } @Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseConditional"); } @Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseDecl"); } @Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseExpr"); } @Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseGroup"); } @Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSigOnly"); } @Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSignals"); } @Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseStoreRef"); } @Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodSpecs"); } @Override public void visitJmlSpecificationCase(JmlSpecificationCase that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlSpecificationCase"); } @Override public void visitJmlStatementLoop(JmlStatementLoop that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStatementLoop"); } @Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) { // scan(that.expression); // scan(that.lo); // scan(that.hi); Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStoreRefArrayRange"); } @Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConditional"); } @Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConstraint"); } @Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseDecl"); } @Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseExpr"); } @Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseIn"); } @Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseInitializer"); } @Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMaps"); } @Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMonitorsFor"); } @Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseRepresents"); } public void visitJmlMethodSig(JmlMethodSig that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlConstraintMethodSig"); } public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlModelProgramStatement"); } // Instead of overriding visitClassDef (which does a lot of processing), a hook // (moreClassDef) was added into the middle of its implementation; this hook does // nothing in the base class, but is overridden below to do additional processing. /** Overridden to be sure that ghost/model declarations within the class are * visited by the flow checker. */ @Override public void moreClassDef(JCClassDecl tree) { // Do nothing if the class is not attibuted if (tree.sym == null) return; // Do nothing if the class is already instrumented for RAC if (Utils.instance(context).isInstrumented(tree.mods.flags)) return; // Do nothing if the class has no specs (i.e., is binary) JmlSpecs.TypeSpecs tspecs = JmlSpecs.instance(context).get(tree.sym); if (tspecs == null) return; JavaFileObject prev = Log.instance(context).currentSourceFile(); try { // Do Flow processing on each JML clause of the class declaration for (JmlTypeClause c : tspecs.clauses) { if (c instanceof JmlTypeClauseDecl) { JCTree d = ((JmlTypeClauseDecl)c).decl; Log.instance(context).useSource(c.source()); if (c.modifiers != null && (c.modifiers.flags & Flags.SYNTHETIC) != 0) { continue; } d.accept(this); } } } finally { Log.instance(context).useSource(prev); } } @Override protected boolean resolveJump(JCTree tree, ListBuffer<AssignAnalyzer.AssignPendingExit> oldPendingExits, JumpKind jk) { boolean resolved = false; List<AssignAnalyzer.AssignPendingExit> exits = pendingExits.toList(); pendingExits = oldPendingExits; for (; exits.nonEmpty(); exits = exits.tail) { PendingExit exit = exits.head; if (exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree) { exit.resolveJump(); resolved = true; } else if(exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree && tree instanceof JmlEnhancedForLoop && // JLS -- adapted to fit JDK8 programming style jk.getTarget(exit.tree) == ((JmlEnhancedForLoop)tree).internalForLoop){ exit.resolveJump(); resolved = true; } else { pendingExits.append((AssignAnalyzer.AssignPendingExit) exit); } } return resolved; } /** Resolve all continues of this statement. */ boolean resolveContinues(JCTree tree) { return resolveJump(tree, new ListBuffer<AssignAnalyzer.AssignPendingExit>(), JumpKind.CONTINUE); } /** Resolve all breaks of this statement. */ boolean resolveBreaks(JCTree tree, ListBuffer<AssignAnalyzer.AssignPendingExit> oldPendingExits) { return resolveJump(tree, oldPendingExits, JumpKind.BREAK); } } class JmlFlowAnalyzer extends FlowAnalyzer implements IJmlVisitor { /** This is a stack of the declarations occurring in JML quantifier expressions, * possible nested (and therefore stacked). */ protected java.util.List<List<JCVariableDecl>> quantDeclStack = new java.util.LinkedList<List<JCVariableDecl>>(); //// These are implemented /** This is overridden in order to handle JML method call-like features (e.g. \typeof) */ @Override public void visitApply(JCMethodInvocation tree) { // tree.meth is null for JML keyword calls if (tree.meth != null) super.visitApply(tree); else scan(tree.args); } @Override public void visitJmlMethodDecl(JmlMethodDecl that) { visitMethodDef(that); } @Override public void visitJmlMethodInvocation(JmlMethodInvocation that) { visitApply(that); } @Override public void visitJmlBinary(JmlBinary that) { scan(that.lhs); scan(that.rhs); } @Override public void visitJmlBlock(JmlBlock that) { scan(that.stats); } @Override public void visitJmlChoose(JmlChoose that) { scan(that.orBlocks); scan(that.elseBlock); } @Override public void visitJmlClassDecl(JmlClassDecl that) { visitClassDef(that); } @Override public void visitJmlCompilationUnit(JmlCompilationUnit that) { visitTopLevel(that); } @Override public void visitJmlDoWhileLoop(JmlDoWhileLoop that) { visitDoLoop(that); } @Override public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) { visitForeachLoop(that); } @Override public void visitJmlForLoop(JmlForLoop that) { visitForLoop(that); } @Override public void visitJmlImport(JmlImport that) { visitImport(that); } @Override public void visitJmlSingleton(JmlSingleton that) { // nothing to do } @Override public void visitJmlLabeledStatement(JmlLabeledStatement that) { // scan(that.extraStatements.toList()); scan(that.body); } @Override public void visitJmlLblExpression(JmlLblExpression that) { scan(that.expression); } @Override public void visitJmlStatement(JmlStatement that) { scan(that.statement); } @Override public void visitJmlStatementDecls(JmlStatementDecls that) { for (JCStatement s: that.list) { scan(s); } } @Override public void visitJmlStatementExpr(JmlStatementExpr that) { // nothing to do for this checker } @Override public void visitJmlStatementHavoc(JmlStatementHavoc that) { scan(that.storerefs); } @Override public void visitJmlVariableDecl(JmlVariableDecl that) { visitVarDef(that); } @Override public void visitJmlWhileLoop(JmlWhileLoop that) { visitWhileLoop(that); } @Override public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) { // No action to take } @Override public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) { // FIXME - check array dimensions? } @Override public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) { // nothing to do ofr this checker } @Override public void visitIdent(JCIdent that) { for (List<JCVariableDecl> list: quantDeclStack) { for (JCVariableDecl decl: list) { if (decl.sym.equals(that.sym)) return; } } super.visitIdent(that); } @Override public void visitJmlSetComprehension(JmlSetComprehension that) { // FIXME: Skipping set comprehension } @Override public void visitJmlStatementSpec(JmlStatementSpec that) { // Is called, but nothing to check } @Override public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) { // FIXME: skipping store-ref expressions // we could call scanExpr on each expression, but we have to watch for special expressions } //// These are not implemented @Override public void visitJmlGroupName(JmlGroupName that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlGroupNameg"); } @Override public void visitJmlMethodClauseCallable(JmlMethodClauseCallable tree) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseCallable"); } @Override public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseConditional"); } @Override public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseDecl"); } @Override public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseExpr"); } @Override public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseGroup"); } @Override public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSigOnly"); } @Override public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseSignals"); } @Override public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodClauseStoreRef"); } @Override public void visitJmlMethodSpecs(JmlMethodSpecs that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlMethodSpecs"); } @Override public void visitJmlSpecificationCase(JmlSpecificationCase that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlSpecificationCase"); } @Override public void visitJmlStatementLoop(JmlStatementLoop that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStatementLoop"); } @Override public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) { // scan(that.expression); // scan(that.lo); // scan(that.hi); Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlStoreRefArrayRange"); } @Override public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConditional"); } @Override public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseConstraint"); } @Override public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseDecl"); } @Override public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseExpr"); } @Override public void visitJmlTypeClauseIn(JmlTypeClauseIn that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseIn"); } @Override public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseInitializer"); } @Override public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMaps"); } @Override public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseMonitorsFor"); } @Override public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlTypeClauseRepresents"); } public void visitJmlMethodSig(JmlMethodSig that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlConstraintMethodSig"); } public void visitJmlModelProgramStatement(JmlModelProgramStatement that) { Log.instance(context).error("jml.internal","Unexpected call of JmlFlow.visitJmlModelProgramStatement"); } // Instead of overriding visitClassDef (which does a lot of processing), a hook // (moreClassDef) was added into the middle of its implementation; this hook does // nothing in the base class, but is overridden below to do additional processing. /** Overridden to be sure that ghost/model declarations within the class are * visited by the flow checker. */ @Override public void moreClassDef(JCClassDecl tree) { // Do nothing if the class is not attibuted if (tree.sym == null) return; // Do nothing if the class is already instrumented for RAC if (Utils.instance(context).isInstrumented(tree.mods.flags)) return; // Do nothing if the class has no specs (i.e., is binary) JmlSpecs.TypeSpecs tspecs = JmlSpecs.instance(context).get(tree.sym); if (tspecs == null) return; JavaFileObject prev = Log.instance(context).currentSourceFile(); try { // Do Flow processing on each JML clause of the class declaration for (JmlTypeClause c : tspecs.clauses) { if (c instanceof JmlTypeClauseDecl) { JCTree d = ((JmlTypeClauseDecl)c).decl; Log.instance(context).useSource(c.source()); if (c.modifiers != null && (c.modifiers.flags & Flags.SYNTHETIC) != 0) { continue; } d.accept(this); } } } finally { Log.instance(context).useSource(prev); } } @Override protected boolean resolveJump(JCTree tree, ListBuffer<FlowAnalyzer.FlowPendingExit> oldPendingExits, JumpKind jk) { boolean resolved = false; List<FlowAnalyzer.FlowPendingExit> exits = pendingExits.toList(); pendingExits = oldPendingExits; for (; exits.nonEmpty(); exits = exits.tail) { PendingExit exit = exits.head; if (exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree) { exit.resolveJump(); resolved = true; } else if(exit.tree.hasTag(jk.treeTag) && jk.getTarget(exit.tree) == tree && tree instanceof JmlEnhancedForLoop && // JLS -- adapted to fit JDK8 programming style jk.getTarget(exit.tree) == ((JmlEnhancedForLoop)tree).internalForLoop){ exit.resolveJump(); resolved = true; } else { pendingExits.append((FlowPendingExit) exit); } } return resolved; } /** Resolve all continues of this statement. */ boolean resolveContinues(JCTree tree) { return resolveJump(tree, new ListBuffer<FlowAnalyzer.FlowPendingExit>(), JumpKind.CONTINUE); } /** Resolve all breaks of this statement. */ boolean resolveBreaks(JCTree tree, ListBuffer<FlowAnalyzer.FlowPendingExit> oldPendingExits) { return resolveJump(tree, oldPendingExits, JumpKind.BREAK); } } // Overridden to call JML versions of visitors @Override public void analyzeTree(Env<AttrContext> env, TreeMaker make) { new JmlAliveAnalyzer().analyzeTree(env, make); new JmlAssignAnalyzer().analyzeTree(env); new JmlFlowAnalyzer().analyzeTree(env, make); new CaptureAnalyzer().analyzeTree(env, make); } }