Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

SpinTrans.java

00001 package edu.ksu.cis.bandera.spin;
00002 
00003 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
00004  * Bandera, a Java(TM) analysis and transformation toolkit           *
00005  * Copyright (C) 1998, 1999   James Corbett (corbett@hawaii.edu)     *
00006  * All rights reserved.                                              *
00007  *                                                                   *
00008  * This work was done as a project in the SAnToS Laboratory,         *
00009  * Department of Computing and Information Sciences, Kansas State    *
00010  * University, USA (http://www.cis.ksu.edu/santos).                  *
00011  * It is understood that any modification not identified as such is  *
00012  * not covered by the preceding statement.                           *
00013  *                                                                   *
00014  * This work is free software; you can redistribute it and/or        *
00015  * modify it under the terms of the GNU Library General Public       *
00016  * License as published by the Free Software Foundation; either      *
00017  * version 2 of the License, or (at your option) any later version.  *
00018  *                                                                   *
00019  * This work is distributed in the hope that it will be useful,      *
00020  * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU *
00022  * Library General Public License for more details.                  *
00023  *                                                                   *
00024  * You should have received a copy of the GNU Library General Public *
00025  * License along with this toolkit; if not, write to the             *
00026  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,      *
00027  * Boston, MA  02111-1307, USA.                                      *
00028  *                                                                   *
00029  * Java is a trademark of Sun Microsystems, Inc.                     *
00030  *                                                                   *
00031  * To submit a bug report, send a comment, or get the latest news on *
00032  * this project and other SAnToS projects, please visit the web-site *
00033  *                http://www.cis.ksu.edu/santos                      *
00034  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00035 import java.io.*;
00036 import java.util.Vector;
00037 import java.util.StringTokenizer;
00038 import java.util.List;
00039 import java.util.LinkedList;
00040 
00041 import edu.ksu.cis.bandera.bir.*;
00042 import edu.ksu.cis.bandera.checker.*;
00043 import edu.ksu.cis.bandera.util.*;
00044 
00045 /**
00046  * SpinTrans is the main class of the SPIN translator, which generates
00047  * PROMELA source representing a transition system.
00048  * <p>
00049  * The SPIN translator is invoked on a transition system as follows:
00050  * <pre>
00051  * // Parameters
00052  * TransSystem system = ...;   // the transition system
00053  * SpinOptions options = new SpinOptions();   // SPIN options
00054  * options.setMemoryLimit(256);            // set various options
00055  * ...
00056  * SpinTrans spin = SpinTrans.translate(system,options);  // invoke translator
00057  * spin.runSpin();                           // Run SPIN on generated PROMELA
00058  * BirTrace trace = spin.parseOutput();      // Parse output as BIR trace
00059  * </pre>
00060  */
00061 
00062 public class SpinTrans extends AbstractExprSwitch implements BirConstants, Checker {
00063   TransSystem system;
00064   PrintWriter out; // Dest for translator output
00065   SpinTypeName namer; // Helper class to name types
00066 
00067   // Print control flags
00068   int indentLevel = 0; // indentation level
00069   boolean inDefine = false; // inside #define (must end line with \)
00070   boolean newLine = true; // at beginning of line (must indent)
00071 
00072   boolean resetTemp = false; // temp used during trans---reset it at end
00073   Transformation currentTrans; // Current trans being translated
00074   int actionNum; // Action number of current trans
00075 
00076   SpinOptions options;
00077   boolean ranVerifier = false;
00078   String panOutput;
00079 
00080   static String ccName = "gcc";
00081 
00082   // This is a special case name that is present in all case trees
00083   public static final String normal = "NORMAL_CASE";
00084 
00085     //private static boolean isWindows = System.getProperty("os.name").indexOf("Windows") >= 0;
00086 
00087     // Robbyjo's patch
00088     private boolean isVerbose = true;
00089     private String outputDir = "."+File.separator;
00090   SpinTrans(TransSystem system, SpinOptions options, PrintWriter out) {
00091     this.system = system;
00092     this.out = out;
00093     this.namer = new SpinTypeName();
00094     this.options = (options == null) ? new SpinOptions() : options;
00095   }  
00096   /**
00097    * Get action ID of current action = loc_num trans_index action_index
00098    */
00099 
00100   String actionId() {
00101     int locNum = currentTrans.getFromLoc().getId();
00102     int transNum = currentTrans.getFromLoc().getOutTrans().indexOf(
00103                      currentTrans);
00104     return locNum + " " + transNum + " " + actionNum;
00105   }  
00106   /**
00107    * Get action ID of current action, but comma seperated.
00108    */
00109 
00110   String actionIdWithCommas() {
00111     int locNum = currentTrans.getFromLoc().getId();
00112     int transNum = currentTrans.getFromLoc().getOutTrans().indexOf(
00113                      currentTrans);
00114     return locNum + "," + transNum + "," + actionNum;
00115   }  
00116   /**
00117    * Apply translator to BIR Expr, return case tree.
00118    */
00119 
00120   CaseNode applyTo(Expr expr) {
00121     expr.apply(this);
00122     return (CaseNode) getResult();
00123   }  
00124   public void caseAddExpr(AddExpr expr) {
00125     translateBinaryOp(expr.getOp1(), expr.getOp2(), " + ");
00126   }  
00127   public void caseAndExpr(AndExpr expr) {
00128     translateBinaryOp(expr.getOp1(), expr.getOp2(), " && ");
00129   }  
00130   /**
00131    * Translate array expression.
00132    * <p>
00133    * Must insert traps for array out of bounds.
00134    */
00135 
00136   public void caseArrayExpr(ArrayExpr expr) {
00137     TreeNode arrayTree = applyTo(expr.getArray());
00138     TreeNode indexTree = applyTo(expr.getIndex());
00139     TreeNode result = arrayTree.compose(indexTree, null);
00140     Vector leafCases = result.getLeafCases(new Vector());
00141     for (int i = 0; i < leafCases.size(); i++) {
00142       Case leafCase = (Case) leafCases.elementAt(i);
00143       ExprNode leaf = (ExprNode) leafCase.node;
00144       // expr1 is array name, expr2 is index
00145       leafCase.insertTrap(leaf.expr2 + " >= " + leaf.expr1 + ".length",
00146                           "ArrayIndexOutOfBoundsException",true);
00147       if (! nonNegative(expr.getIndex()))
00148         leafCase.insertTrap(leaf.expr2 + " < 0", "ArrayIndexOutOfBoundsException",
00149                             true);
00150       leaf.update(leaf.expr1 + ".element[" + leaf.expr2 + "]");
00151     }
00152     setResult(result);
00153   }  
00154   /**
00155    * Translate assert action.
00156    */
00157 
00158   public void caseAssertAction(AssertAction assertAction) {
00159     CaseNode condTree = (CaseNode) applyTo(assertAction.getCondition());
00160     Vector leaves = condTree.getLeaves(new Vector());
00161     // Assert each leaf is true
00162     for (int i = 0; i < leaves.size(); i++) {
00163       ExprNode leaf = (ExprNode) leaves.elementAt(i);
00164       leaf.update("assert(" + leaf.expr1 + ");");
00165     }
00166     printStatement(condTree.getCase(normal));
00167   }  
00168   /**
00169    * Translate a BIR assignment action.
00170    */
00171 
00172   public void caseAssignAction(AssignAction assign) {
00173     TreeNode rhsTree = applyTo(assign.getRhs());
00174     TreeNode lhsTree = applyTo(assign.getLhs());
00175     CaseNode result = (CaseNode) lhsTree.compose(rhsTree, null);
00176     Vector leafCases = result.getLeafCases(new Vector());
00177     for (int i = 0; i < leafCases.size(); i++) {
00178       Case leafCase = (Case) leafCases.elementAt(i);
00179       ExprNode leaf = (ExprNode) leafCase.node;
00180       Type rhsType = assign.getRhs().getType();
00181       Type lhsType = assign.getLhs().getType();
00182       // Make run-time checks on assignment if not subtype
00183       if (! rhsType.isSubtypeOf(lhsType)) {
00184 
00185         // Check subrange for limit exception
00186         if (lhsType.isKind(RANGE) &&
00187             ! lhsType.containsValue(assign.getRhs())) {
00188           Range range = (Range) lhsType;
00189           leafCase.insertTrap(leaf.expr2 + " < " +
00190                               range.getFromVal(), "RangeLimitException",
00191                               false);
00192           leafCase.insertTrap(leaf.expr2 + " > " +
00193                               range.getToVal(), "RangeLimitException", false);
00194         }
00195 
00196         // Check ref assignment for class cast exception
00197         if (lhsType.isKind(REF) &&
00198             ! (assign.getRhs() instanceof NullExpr)) {
00199           String trapDesc = "ClassCastException";
00200           StateVarVector lhsTargets =
00201             ((Ref) lhsType).getTargets();
00202           StateVarVector rhsTargets =
00203             ((Ref) rhsType).getTargets();
00204           for (int j = 0; j < rhsTargets.size(); j++)
00205             if (! lhsTargets.contains(
00206                   rhsTargets.elementAt(j))) {
00207               String check =
00208                 "_collect(" + leaf.expr2 + ") == " +
00209                 refIndex(rhsTargets.elementAt(j));
00210               leafCase.insertTrap(check, "ClassCastException",
00211                                   true);
00212             }
00213         }
00214       }
00215       leaf.update(leaf.expr1 + " = " + leaf.expr2 + ";");
00216     }
00217     printStatement(result.getCase(normal));
00218   }  
00219   public void caseBoolLit(BoolLit expr) {
00220     setResult(specialize("" + expr.getValue()));
00221   }  
00222   /**
00223    * Translate choose expression.
00224    * <p>
00225    * As in NexExpr, we spit out code (in this case a nondeterministic
00226    * if statement) that sets the _temp_ variable of the process.
00227    * We then return _temp_ as the value of the expression.
00228    * Once again, this is safe because a choose() must appear
00229    * alone on the RHS of an assign action.
00230    */
00231 
00232   public void caseChooseExpr(ChooseExpr expr) {
00233     Vector choices = expr.getChoices();
00234     println("if");
00235     for (int i = 0; i < choices.size(); i++) {
00236       Expr choice = (Expr) choices.elementAt(i);
00237       ExprNode leaf = (ExprNode) applyTo(choice).getCase(normal);
00238       println(":: _temp_ = " + leaf.expr1 + "; printf(\"BIR? " +
00239               actionNum + " " + i + "\\n\"); ");
00240     }
00241     println("fi;");
00242     resetTemp = true;
00243     setResult(specialize("_temp_"));
00244   }  
00245   public void caseConstant(Constant expr) {
00246     setResult(specialize(expr.getName()));
00247   }  
00248   /**
00249    * Translate dereference.
00250    * <p>
00251    * This is the case that creates most of the CaseNodes in
00252    * a case tree.  We need to branch based on the collection
00253    * of the target.
00254    */
00255 
00256   public void caseDerefExpr(DerefExpr expr) {
00257     TreeNode result = applyTo(expr.getTarget());
00258     StateVarVector targets =
00259       ((Ref) expr.getTarget().getType()).getTargets();
00260     Vector leafCases = result.getLeafCases(new Vector());
00261     for (int i = 0; i < leafCases.size(); i++) {
00262       Case leafCase = (Case) leafCases.elementAt(i);
00263       ExprNode leaf = (ExprNode) leafCase.node;
00264       CaseNode caseNode = new CaseNode();
00265       // Loop through the possible targets, making a case for each
00266       for (int j = 0; j < targets.size(); j++) {
00267         StateVar target = targets.elementAt(j);
00268         String prom = target.getName();
00269         // For collections, add the instance selection
00270         if (target.getType().isKind(COLLECTION))
00271           prom += ".instance[_index(" + leaf.expr1 + ")]";
00272         String cond = "(_collect(" + leaf.expr1 + ") == " +
00273                       refIndex(target) + ")";
00274         caseNode.addCase(cond, prom);
00275       }
00276       // Collection 0 is the null pointer
00277       String cond = "(_collect(" + leaf.expr1 + ") == 0)";
00278       caseNode.addTrapCase(cond, "NullPointerException", true);
00279       leafCase.replace(caseNode);
00280     }
00281     setResult(result);
00282   }  
00283   /**
00284    * Translate division.
00285    * <p>
00286    * DivExpr and RemExpr require special handling because they
00287    * can cause a ArithmeticException if the second arg is zero.
00288    */
00289 
00290   public void caseDivExpr(DivExpr expr) {
00291     TreeNode e1Tree = applyTo(expr.getOp1());
00292     TreeNode e2Tree = applyTo(expr.getOp2());
00293     TreeNode result = e1Tree.compose(e2Tree, null);
00294     Vector leafCases = result.getLeafCases(new Vector());
00295     for (int i = 0; i < leafCases.size(); i++) {
00296       Case leafCase = (Case) leafCases.elementAt(i);
00297       ExprNode leaf = (ExprNode) leafCase.node;
00298       leafCase.insertTrap(leaf.expr2 + " == 0", "ArithmeticException",
00299                           true);
00300       leaf.update("(" + leaf.expr1 + " / " + leaf.expr2 + ")");
00301     }
00302     setResult(result);
00303   }  
00304   public void caseEqExpr(EqExpr expr) {
00305     translateBinaryOp(expr.getOp1(), expr.getOp2(), " == ");
00306   }  
00307   /**
00308    * Translate instanceof expression (basically a big disjunction to
00309    * test the refIndex for all possible targets).
00310    */
00311 
00312   public void caseInstanceOfExpr(InstanceOfExpr expr) {
00313     TreeNode result = applyTo(expr.getRefExpr());
00314     StateVarVector targets = expr.getRefType().getTargets();
00315     Vector leafCases = result.getLeafCases(new Vector());
00316     for (int i = 0; i < leafCases.size(); i++) {
00317       Case leafCase = (Case) leafCases.elementAt(i);
00318       ExprNode leaf = (ExprNode) leafCase.node;
00319       CaseNode caseNode = new CaseNode();
00320       String elseCond = "! (";
00321       for (int j = 0; j < targets.size(); j++) {
00322         StateVar target = targets.elementAt(j);
00323         String cond = "(_collect(" + leaf.expr1 + ") == " +
00324                       refIndex(target) + ")";
00325         caseNode.addCase(cond, "1");
00326         if (j > 0)
00327           elseCond += " || ";
00328         elseCond += cond;
00329       }
00330       elseCond += ")";
00331       caseNode.addCase(elseCond, "0");
00332       leafCase.replace(caseNode);
00333     }
00334     setResult(result);
00335   }  
00336   public void caseIntLit(IntLit expr) {
00337     setResult(specialize("" + expr.getValue()));
00338   }  
00339   public void caseLeExpr(LeExpr expr) {
00340     translateBinaryOp(expr.getOp1(), expr.getOp2(), " <= ");
00341   }  
00342   /**
00343    * Translate array length expression.
00344    */
00345 
00346   public void caseLengthExpr(LengthExpr expr) {
00347     TreeNode result = applyTo(expr.getArray());
00348     Vector leaves = result.getLeaves(new Vector());
00349     for (int i = 0; i < leaves.size(); i++) {
00350       ExprNode leaf = (ExprNode) leaves.elementAt(i);
00351       leaf.update(leaf.expr1 + ".length");
00352     }
00353     setResult(result);
00354   }  
00355   /**
00356    * Translate lock action.
00357    */
00358 
00359   public void caseLockAction(LockAction lockAction) {
00360     CaseNode lockTree = (CaseNode) applyTo(lockAction.getLockExpr());
00361     String opName =
00362       "_" + LockAction.operationName(lockAction.getOperation());
00363     // Wait action requires specific thread id
00364     if (lockAction.isWait())
00365       opName += "_" + lockAction.getThread().getId();
00366     Lock lockType = (Lock) lockAction.getLockExpr().getType();
00367     // Reentrant locks require _R versions of operations (except notify)
00368     if (lockType.isReentrant() &&
00369         ! lockAction.isLockAction(NOTIFY | NOTIFYALL))
00370       opName += "_R";
00371     Vector leaves = lockTree.getLeaves(new Vector());
00372     for (int i = 0; i < leaves.size(); i++) {
00373       ExprNode leaf = (ExprNode) leaves.elementAt(i);
00374       // Apply op to each leaf expr
00375       leaf.update(opName + "(" + leaf.expr1 + "," +
00376                   actionIdWithCommas() + ");");
00377     }
00378     printStatement(lockTree.getCase(normal));
00379   }  
00380   /**
00381    * Translate lock test.
00382    */
00383 
00384   public void caseLockTest(LockTest lockTest) {
00385     TreeNode lockTree;
00386     Vector leaves;
00387     switch (lockTest.getOperation()) {
00388       case LOCK_AVAILABLE:
00389         String opName = "_lockAvailable";
00390         // Reentrant locks require special _R versions of operations
00391         if (((Lock) lockTest.getLockExpr().getType()).isReentrant())
00392           opName += "_R";
00393         lockTree = applyTo(lockTest.getLockExpr());
00394         leaves = lockTree.getLeaves(new Vector());
00395         for (int i = 0; i < leaves.size(); i++) {
00396           ExprNode leaf = (ExprNode) leaves.elementAt(i);
00397           leaf.update(opName + "(" + leaf.expr1 + ")");
00398         }
00399         setResult(lockTree);
00400         return;
00401       case HAS_LOCK:
00402         // We don't use this test (it's always true)
00403         setResult(specialize("true"));
00404         return;
00405       case WAS_NOTIFIED:
00406         lockTree = applyTo(lockTest.getLockExpr());
00407         leaves = lockTree.getLeaves(new Vector());
00408         for (int i = 0; i < leaves.size(); i++) {
00409           ExprNode leaf = (ExprNode) leaves.elementAt(i);
00410           leaf.update("_wasNotified_" +
00411                       lockTest.getThread().getId() + "(" +
00412                       leaf.expr1 + ")");
00413         }
00414         setResult(lockTree);
00415         return;
00416     }
00417   }  
00418   public void caseLtExpr(LtExpr expr) {
00419     translateBinaryOp(expr.getOp1(), expr.getOp2(), " < ");
00420   }  
00421   public void caseMulExpr(MulExpr expr) {
00422     translateBinaryOp(expr.getOp1(), expr.getOp2(), " * ");
00423   }  
00424   public void caseNeExpr(NeExpr expr) {
00425     translateBinaryOp(expr.getOp1(), expr.getOp2(), " != ");
00426   }  
00427   /**
00428    * Translate array allocator.
00429    * <p>
00430    * Similar to NewExpr (above), except that we need to set (and check)
00431    * the supplied length.
00432    */
00433 
00434   public void caseNewArrayExpr(NewArrayExpr expr) {
00435     StateVar target = expr.getCollection();
00436     int size = ((Collection) target.getType()).getSize().getValue();
00437     int locNum = currentTrans.getFromLoc().getId();
00438     int transNum = currentTrans.getFromLoc().getOutTrans().indexOf(
00439                      currentTrans);
00440     // Call macro _allocate(collection,refindex,size,locNum,transNum,actionNum)
00441     println("_allocate(" + target.getName() + "," +
00442             refIndex(target) + "," + size + "," + locNum + "," +
00443             transNum + "," + actionNum + ");");
00444     Array type = (Array)((Collection) target.getType()).getBaseType();
00445     int maxLength = type.getSize().getValue();
00446     // Translate the length expression, then update each leaf to
00447     // (a) check that the length is less than the max for this type
00448     // (b) if so, assign the supplied length to the .length field
00449     //     of the array instance just allocated (_temp_).
00450     CaseNode setLength = applyTo(expr.getLength());
00451     Vector leafCases = setLength.getLeafCases(new Vector());
00452     for (int i = 0; i < leafCases.size(); i++) {
00453       Case leafCase = (Case) leafCases.elementAt(i);
00454       ExprNode leaf = (ExprNode) leafCase.node;
00455       leafCase.insertTrap(leaf.expr1 + " > " + maxLength, "ArraySizeLimitException",
00456                           false);
00457       leaf.update(target.getName() +
00458                   ".instance[_index(_temp_)].length = " +
00459                   leaf.expr1 + "; printf(\"BIR? " + actionNum +
00460                   " %d\\n\"," + leaf.expr1 + ");");
00461     }
00462     // Generate the code that sets the array length
00463     printStatement(setLength.getCase(normal));
00464     resetTemp = true;
00465     setResult(specialize("_temp_"));
00466   }  
00467   /**
00468    * Translate an allocator.
00469    * <p>
00470    * When we translate the allocator expression, we spit out a
00471    * call to the _allocate() macro, which selects a free instance
00472    * of the collection and assigns the instance index to _temp_.
00473    * We then just return "_temp_" as the value of this expression
00474    * (this is OK since allocators must appear alone on the RHS
00475    * of an assignment action).
00476    */
00477 
00478   public void caseNewExpr(NewExpr expr) {
00479     StateVar target = expr.getCollection();
00480     int size = ((Collection) target.getType()).getSize().getValue();
00481     int locNum = currentTrans.getFromLoc().getId();
00482     int transNum = currentTrans.getFromLoc().getOutTrans().indexOf(
00483                      currentTrans);
00484     // Call macro _allocate(collection,refindex,size,locNum,transNum,actionNum)
00485     println("_allocate(" + target.getName() + "," +
00486             refIndex(target) + "," + size + "," + locNum + "," +
00487             transNum + "," + actionNum + ");");
00488     resetTemp = true;
00489     setResult(specialize("_temp_"));
00490   }  
00491   public void caseNotExpr(NotExpr expr) {
00492     translateUnaryOp(expr.getOp(), "! ");
00493   }  
00494   public void caseNullExpr(NullExpr expr) {
00495     setResult(specialize("0"));
00496   }  
00497   public void caseOrExpr(OrExpr expr) {
00498     translateBinaryOp(expr.getOp1(), expr.getOp2(), " || ");
00499   }  
00500   /**
00501    * Translate BIR Print action.
00502    * <p>
00503    * Perhaps remove this---easier to just get output from BIR trace instead.
00504    */
00505 
00506   public void casePrintAction(PrintAction printAction) {
00507     if (printAction.getPrintItems().size() > 0) {
00508       String format = "BIR|";
00509       String params = "";
00510       Vector printItems = printAction.getPrintItems();
00511       for (int i = 0; i < printItems.size(); i++) {
00512         Object item = printItems.elementAt(i);
00513         if (item instanceof String)
00514           format += item;
00515         else {
00516           format += "%d";
00517           CaseNode varTree = (CaseNode) applyTo((Expr) item);
00518           params +=
00519             "," + ((ExprNode) varTree.getCase(normal)).expr1;
00520         }
00521       }
00522       println("printf(\"" + format + "\\n\"" + params + "); ");
00523     }
00524   }  
00525   /**
00526    * Translate field selector.
00527    */
00528 
00529   public void caseRecordExpr(RecordExpr expr) {
00530     TreeNode result = applyTo(expr.getRecord());
00531     String field = expr.getField().getName();
00532     Vector leaves = result.getLeaves(new Vector());
00533     for (int i = 0; i < leaves.size(); i++) {
00534       ExprNode leaf = (ExprNode) leaves.elementAt(i);
00535       leaf.update(leaf.expr1 + "." + field);
00536     }
00537     setResult(result);
00538   }  
00539   public void caseRefExpr(RefExpr expr) {
00540     setResult(specialize("_ref(" + refIndex(expr.getTarget()) + ",0)"));
00541   }  
00542   public void caseRemExpr(RemExpr expr) {
00543     TreeNode e1Tree = applyTo(expr.getOp1());
00544     TreeNode e2Tree = applyTo(expr.getOp2());
00545     TreeNode result = e1Tree.compose(e2Tree, null);
00546     Vector leafCases = result.getLeafCases(new Vector());
00547     for (int i = 0; i < leafCases.size(); i++) {
00548       Case leafCase = (Case) leafCases.elementAt(i);
00549       ExprNode leaf = (ExprNode) leafCase.node;
00550       leafCase.insertTrap(leaf.expr2 + " == 0", "ArithmeticException",
00551                           true);
00552       leaf.update("(" + leaf.expr1 + " % " + leaf.expr2 + ")");
00553     }
00554     setResult(result);
00555   }  
00556   public void caseStateVar(StateVar expr) {
00557     setResult(specialize(expr.getName()));
00558   }  
00559   public void caseSubExpr(SubExpr expr) {
00560     translateBinaryOp(expr.getOp1(), expr.getOp2(), " - ");
00561   }  
00562   /**
00563    * Translate thread action.
00564    */
00565 
00566   public void caseThreadAction(ThreadAction threadAction) {
00567     String opName =
00568       ThreadAction.operationName(threadAction.getOperation());
00569     int threadId = threadAction.getThreadArg().getId();
00570     println("_" + opName + "Thread_" + threadId + ";");
00571   }  
00572   /**
00573    * Translate thread location test.
00574    */
00575 
00576   public void caseThreadLocTest(ThreadLocTest threadLocTest) {
00577     String threadName = threadLocTest.getThread().getName();
00578     int threadId = 1 + threadLocTest.getThread().getId();
00579     String locLabel = locLabel(threadLocTest.getLocation());
00580     // PROMELA syntax:  thread[pid]@labal
00581     String test = threadName + "[" + threadId + "]@" + locLabel;
00582     setResult(specialize(test));
00583   }  
00584   /**
00585    * Translate thread test.
00586    */
00587 
00588   public void caseThreadTest(ThreadTest threadTest) {
00589     switch (threadTest.getOperation()) {
00590       case THREAD_TERMINATED:
00591         String opName = ThreadTest.operationName(
00592                           threadTest.getOperation());
00593         int threadId = threadTest.getThreadArg().getId();
00594         // Just call the #define'd macro for this thread
00595         String test = "_" + opName + "_" + threadId;
00596         setResult(specialize(test));
00597         return;
00598     }
00599   }  
00600 public String check() {
00601     String systemName = system.getName();
00602     String promFilename = systemName + ".prom";
00603     String trailFilename = promFilename + ".trail";
00604 
00605     if (options.ApplyNeverClaim) {
00606         // Translate LTL into never claim
00607         String ltlFilename = systemName + ".ltl";
00608         File ltlFile = new File(ltlFilename);
00609         if (!ltlFile.exists())
00610             throw new RuntimeException("Cannot find LTL file: "+ltlFile.getAbsolutePath());
00611 
00612         try {
00613             String neverClaim = BanderaUtil.exec("spin -F " + ltlFilename, true);
00614             PrintWriter pw = new PrintWriter(new FileWriter(systemName + ".nvr"));
00615             pw.print(neverClaim);
00616             pw.flush();
00617             pw.close();
00618         } catch (Exception e)
00619         {
00620             throw new RuntimeException("Cannot produce file '"+systemName+"',");
00621         }
00622     }
00623 
00624     File sourceFile = new File(promFilename);
00625     if (!sourceFile.exists())
00626         throw new RuntimeException("Cannot find SPIN source file: " + sourceFile.getAbsolutePath());
00627 
00628     /* // Execute cpp
00629     execAndWait("cpp -P " + promFilename + " tmp", true);
00630     File promFile = new File(promFilename);
00631     if (!promFile.delete() || !new File("tmp").renameTo(promFile))
00632         throw new RuntimeException("Could not rename tmp to " + promFilename);
00633     */
00634 
00635     StringBuffer output = new StringBuffer();
00636 
00637     try {
00638         output.append(BanderaUtil.exec("spin -a " + promFilename, isVerbose));
00639         if (!(new File("pan.c")).exists()) throw new RuntimeException();
00640     } catch (Exception e) {
00641         cleanMess();
00642         throw new RuntimeException("Could not produce pan.c file!");
00643     }
00644 
00645     try {
00646         output.append(BanderaUtil.exec(ccName + " -O3 " + options.compileOptions2() + " -o pan.exe pan.c", isVerbose));
00647         if (!(new File("pan.exe")).exists()) throw new RuntimeException();
00648     } catch (Exception e) {
00649         cleanMess();
00650         throw new RuntimeException("Could not compile pan.c!");
00651     }
00652 
00653     String panOutput;
00654     try {
00655         // Return output of 'pan'
00656         panOutput = BanderaUtil.exec("pan.exe " + options.panOptions(), isVerbose);
00657         output.append(panOutput);
00658     } catch (Exception e)
00659     {
00660          try {
00661                 cleanMess();
00662          } catch (Exception exx){}
00663         throw new RuntimeException("Failed to run pan.exe!");
00664     }
00665     cleanMess();
00666 
00667     if (panOutput.indexOf("error : max search depth too small") >= 0) {
00668         throw new RuntimeException("Verifier search depth exceeded!");
00669     } else if (panOutput.indexOf("error : out of memory") >= 0) {
00670         throw new RuntimeException("Verifier ran out of memory!");
00671     } else if (panOutput.indexOf("error : VECTORSZ is too small") >= 0) {
00672         throw new RuntimeException("Verifier vector exceeded!");
00673     } else if (panOutput.indexOf("Search not completed") >= 0 && panOutput.indexOf("pan: wrote") < 0) {
00674          // Search not complete, but it doesn't write any counter example trail
00675         throw new RuntimeException("Verifier cannot complete its run:"+System.getProperty("line.separator")+panOutput);
00676     }
00677 
00678     try {
00679         BanderaUtil.moveFile(trailFilename, outputDir);
00680     } catch (Exception e)
00681     {
00682         throw new RuntimeException("Cannot move the counter example trail into the output directory!");
00683     }
00684 
00685     return output.toString();
00686 }
00687 private void cleanMess()
00688 {
00689     // Sweep
00690     String systemName = system.getName();
00691     String ltlFilename = systemName + ".ltl";
00692     String nvrFilename = systemName + ".nvr";
00693     String promFilename = systemName + ".prom";
00694     String birFilename = systemName + ".bir";
00695     String trailFilename = promFilename + ".trail";
00696 
00697     if (!".".equals(outputDir) && !("."+File.separator).equals(outputDir))
00698     {
00699         new File(outputDir+birFilename).delete();
00700         new File(outputDir+ltlFilename).delete();
00701         new File(outputDir+nvrFilename).delete();
00702         new File(outputDir+promFilename).delete();
00703         new File(outputDir+trailFilename).delete();
00704         new File(outputDir+"pan.exe").delete();
00705         new File(outputDir+"pan.b").delete();
00706         new File(outputDir+"pan.c").delete();
00707         new File(outputDir+"pan.h").delete();
00708         new File(outputDir+"pan.m").delete();
00709         new File(outputDir+"pan.t").delete();
00710     }
00711     BanderaUtil.moveFile(birFilename, outputDir);
00712     BanderaUtil.moveFile(ltlFilename, outputDir);
00713     BanderaUtil.moveFile(nvrFilename, outputDir);
00714     BanderaUtil.moveFile(promFilename, outputDir);
00715     BanderaUtil.moveFile(trailFilename, outputDir);
00716     BanderaUtil.moveFile("pan.exe", outputDir);
00717     BanderaUtil.moveFile("pan.b", outputDir);
00718     BanderaUtil.moveFile("pan.c", outputDir);
00719     BanderaUtil.moveFile("pan.h", outputDir);
00720     BanderaUtil.moveFile("pan.m", outputDir);
00721     BanderaUtil.moveFile("pan.t", outputDir);
00722 }
00723 
00724   static String currentDir() {
00725     return System.getProperty("user.dir");
00726   }  
00727   public void defaultCase(Object obj) {
00728     throw new RuntimeException("Trans type not handled: " + obj);
00729   }  
00730   /**
00731    * Generate constant and type definitions (using type namer).
00732    */
00733 
00734   void definitions() {
00735     Vector definitions = system.getDefinitions();
00736 
00737     // For each Constant, generate a #define
00738     for (int i = 0; i < definitions.size(); i++) {
00739       Definition def = (Definition) definitions.elementAt(i);
00740       if (def instanceof Constant)
00741         println("#define " + def.getName() + " " + def.getDef());
00742     }
00743 
00744     Vector types = system.getTypes();
00745     for (int i = 0; i < types.size(); i++) {
00746       Type type = (Type) types.elementAt(i);
00747       // For non-composite types, generate a #define for the type name
00748       if (type.isKind(BOOL | RANGE | LOCK | ENUMERATED | REF)) {
00749         println("/* " + type.typeName() + " = " + type + " */");
00750         print("#define " + type.typeName() + " ");
00751         type.apply(namer, this);
00752         println((String) namer.getResult());
00753         // For enumerated types, also #define the named constants
00754         if (type.isKind(ENUMERATED)) {
00755           Enumerated enum = (Enumerated) type;
00756           for (int j = 0; j < enum.getEnumeratedSize(); j++) {
00757             String name = enum.getNameOf(
00758                             enum.getFirstElement() + j);
00759             if (name != null)
00760               println("#define " + name + " " +
00761                       (enum.getFirstElement() + j));
00762           }
00763         }
00764       }
00765     }
00766 
00767     // For composite types (record, array, collection), generate
00768     // a typedef (struct).
00769     for (int i = 0; i < types.size(); i++) {
00770       Type type = (Type) types.elementAt(i);
00771       if (! type.isKind(BOOL | RANGE | LOCK | ENUMERATED | REF)) {
00772         println("/* " + type.typeName() + " = " + type + " */");
00773         print("typedef " + type.typeName() + " ");
00774         type.apply(namer, this);
00775         println((String) namer.getResult());
00776       }
00777     }
00778     println();
00779   }  
00780 /**
00781  * getCounterExample method comment.
00782  */
00783 public List getCounterExample() {
00784     String systemName = system.getName();
00785     String baseName = outputDir + systemName;
00786     String promFilename = baseName + ".prom";
00787     String trailFilename = promFilename + ".trail";
00788 
00789     File trail = new File(trailFilename);
00790     if (!trail.exists())
00791     {
00792          return null;
00793 /*       trail = new File(outputDir+"."+systemName+".prom.trail");
00794          if (!trail.exists()) return null;
00795          trail.renameTo(new File(trailFilename));
00796 */
00797     }
00798 
00799     String output = BanderaUtil.exec("spin -t " + promFilename, true);
00800     BufferedReader reader = new BufferedReader(new StringReader(output));
00801     LinkedList ll = new LinkedList();
00802     String ln = System.getProperty("line.separator");
00803     try {
00804         String line;
00805         do {
00806             line = reader.readLine();
00807             if (line != null && line.trim().startsWith("BIR")) ll.add(line.trim());
00808         } while (line != null);
00809     } catch (Exception e)
00810     {
00811     }
00812     return ll;
00813 }
00814   /**
00815    * Retrieve simple result from translator, flag error if not simple.
00816    * <p>
00817    * A simple result is a case tree with only a single expression
00818    * node under the NORMAL case.
00819    */
00820 
00821   String getSimpleResult() {
00822     TreeNode result = ((CaseNode) getResult()).getCase(normal);
00823     if (result instanceof ExprNode)
00824       return ((ExprNode) result).expr1;
00825     else
00826       throw new RuntimeException("Result not simple: " + result);
00827   }  
00828   public SpinOptions getSpinOptions() {
00829     return options;
00830   }  
00831   /**
00832    * Test if guard is present.
00833    * <p>
00834    * If the guard is TRUE (BoolLit) or the HAS_LOCK test (which we
00835    * don't use), claim there is no guard.
00836    */
00837 
00838   boolean guardPresent(Transformation trans) {
00839     Expr guard = trans.getGuard();
00840     if (guard == null)
00841       return false;
00842     if (guard instanceof BoolLit)
00843       return ! ((BoolLit) guard).getValue();
00844     if ((guard instanceof LockTest) &&
00845         ((LockTest) guard).getOperation() == HAS_LOCK)
00846       return false;
00847     return true;
00848   }  
00849   /**
00850    * Generate the header for all PROMELA files
00851    */
00852 
00853   void header() {
00854     ThreadVector threadVector = system.getThreads();
00855 
00856     println("/*  Promela for transition system: " +
00857             system.getName() + " */");
00858     println();
00859 
00860     // Lock types (R = reentrant, W = waiting)
00861     println("/* Lock operations */");
00862     println("#define LOCK 0");
00863     println("typedef lock_ {\n  chan lock = [1] of { bit };\n  byte owner;\n};");
00864     println("typedef lock_RW {\n  chan lock = [1] of { bit };\n  byte owner;\n  int waiting;\n  byte count;\n};");
00865     println("typedef lock_W {\n  chan lock = [1] of { bit };\n  byte owner;\n  int waiting;\n};");
00866     println("typedef lock_R {\n  chan lock = [1] of { bit };\n  byte owner;\n  byte count;\n};");
00867 
00868     // Monitor operations - most have separate reentrant version (_R)
00869     println("#define _lock(sync,locNum,transNum,actionNum) \\");
00870     println("  sync.lock?LOCK; sync.owner = _pid ");
00871     println("#define _lock_R(sync,locNum,transNum,actionNum) \\");
00872     println("  if \\");
00873     println("  :: sync.owner == _pid -> sync.count++; \\");
00874     println("  :: else -> sync.lock?LOCK; sync.owner = _pid; \\");
00875     println("  fi ");
00876 
00877     println("#define _unlock(sync,locNum,transNum,actionNum) \\");
00878     println("  sync.owner = 0; sync.lock!LOCK ");
00879     println("#define _unlock_R(sync,locNum,transNum,actionNum) \\");
00880     println("  if \\");
00881     println("  :: sync.count > 0 -> sync.count--; \\");
00882     println("  :: else -> sync.owner = 0; sync.lock!LOCK; \\");
00883     println("  fi ");
00884 
00885     println("#define _lockAvailable(sync) \\");
00886     println("  nempty(sync.lock)");
00887     println("#define _lockAvailable_R(sync) \\");
00888     println("  (nempty(sync.lock) || sync.owner == _pid) ");
00889 
00890     println("#define _unwait(sync,locNum,transNum,actionNum) \\");
00891     println("  sync.lock?LOCK; sync.owner = _pid ");
00892     println("#define _unwait_R(sync,locNum,transNum,actionNum) \\");
00893     println("  sync.lock?LOCK; sync.owner = _pid; sync.count = _temp_; _temp_ = 0 ");
00894 
00895     println("#define _notifyAll(sync,locNum,transNum,actionNum) \\");
00896     println("  if \\");
00897     println("  :: sync.owner == _pid -> sync.waiting = 0; \\");
00898     println("  :: else -> printf(\"BIR: locNum transNum actionNum IllegalMonitorStateException\\n\"); assert(0); \\");
00899     println("  fi ");
00900 
00901     println("#define _notify(sync,locNum,transNum,actionNum) \\");
00902     println("  if \\");
00903     for (int i = 0; i < threadVector.size(); i++) {
00904       int thread = (1 << i);
00905       println("  :: (sync.owner == _pid) && (sync.waiting & " +
00906               thread + ") -> sync.waiting = sync.waiting & " +
00907               ~ thread + " ; printf(\"BIR? %d " + i + "\",actionNum); \\");
00908     }
00909     println(
00910       "  :: (sync.owner == _pid) && (sync.waiting == 0) -> printf(\"BIR? %d " +
00911       threadVector.size() + "\\n\",actionNum); \\");
00912     println("  :: else -> printf(\"BIR: locNum transNum actionNum IllegalMonitorStateException\\n\"); assert(0); \\");
00913     println("  fi  ");
00914 
00915     for (int i = 0; i < threadVector.size(); i++) {
00916       int thread = (1 << i);
00917 
00918       println("#define _wait_" + i + "(sync,locNum,transNum,actionNum) \\");
00919       println("  if \\");
00920       println(
00921         "  :: sync.owner == _pid -> sync.waiting = sync.waiting | " +
00922         thread + ";  \\");
00923       println("       sync.owner = 0; sync.lock!LOCK; \\");
00924       println("  :: else -> printf(\"BIR: locNum transNum actionNum IllegalMonitorStateException\\n\"); assert(0); \\");
00925       println("  fi ");
00926       println("#define _wait_" + i + "_R(sync,locNum,transNum,actionNum) \\");
00927       println("  if \\");
00928       println(
00929         "  :: sync.owner == _pid -> sync.waiting = sync.waiting | " +
00930         thread + ";  \\");
00931       println("       _temp_ = sync.count; sync.count = 0; sync.owner = 0; sync.lock!LOCK; \\");
00932       println("  :: else -> printf(\"BIR: locNum transNum actionNum IllegalMonitorStateException\\n\"); assert(0); \\");
00933       println("  fi ");
00934 
00935       println("#define _wasNotified_" + i + "(sync) \\");
00936       println("  !(sync.waiting & " + thread + ") ");
00937 
00938       // Thread operations
00939       if (! threadVector.elementAt(i).isMain()) {
00940         println("#define _startThread_" + i + " \\");
00941         println("  _threadActive_" + i +
00942                 " = true; _threadStart_" + i + "!LOCK");
00943         println("#define _beginThread_" + i + " \\");
00944         println("  _threadStart_" + i + "?LOCK");
00945         println("#define _joinThread_" + i + " \\");
00946         println("  skip");
00947         println("#define _exitThread_" + i + " \\");
00948         println("  _threadActive_" + i + " = 0; goto endtop_" + i);
00949         println("#define _threadTerminated_" + i + " \\");
00950         println("  (_threadActive_" + i + " == 0)");
00951       }
00952     }
00953 
00954     // Reference macros
00955     println();
00956     println("/* Reference operations */");
00957     println("#define _collect(r) (r >> 8)");
00958     println("#define _index(r) (r & 255)");
00959     println("#define _ref(c,i) ((c << 8) | i)");
00960 
00961     // Allocator macro
00962     //   _i_ is always zero outside of this macro
00963     println("#define _allocate(collection,refindex,maxsize,locNum,transNum,actionNum) \\");
00964     println("  do \\");
00965     println("  :: collection.inuse[_i_] -> \\");
00966     println("     _i_ = _i_ + 1; \\");
00967     println("     if  \\");
00968     println("     :: _i_ == maxsize -> printf(\"BIR: locNum transNum actionNum  CollectionSizeLimitException\\n\"); limit_exception = 1; _i_ = 0; goto endtrap; \\");
00969     println("     :: else \\");
00970     println("     fi; \\");
00971     println("  :: else -> \\");
00972     println("     collection.inuse[_i_] = 1; \\");
00973     println("     _temp_ = _ref(refindex,_i_);  \\");
00974     println("     _i_ = 0;  \\");
00975     println("     break;  \\");
00976     println("  od ");
00977     println();
00978     println("#define TRAP 1");
00979     println();
00980   }  
00981   /**
00982    * Generate init {} block to initializae variables and start threads.
00983    */
00984 
00985   void initBlock() {
00986     ThreadVector threadVector = system.getThreads();
00987     println("init {");
00988     indentLevel = 2;
00989     println("atomic {");
00990     indentLevel++;
00991     StateVarVector stateVarVector = system.getStateVars();
00992 
00993     // Use variable initializer to init each state var
00994     SpinTypeInit initializer = new SpinTypeInit(this, out);
00995     for (int i = 0; i < stateVarVector.size(); i++) {
00996       StateVar var = stateVarVector.elementAt(i);
00997       var.getType().apply(initializer, var);
00998     }
00999 
01000     // Start each thread proctype (making sure its ID is
01001     // what we think it should be---else the location tests will fail).
01002     for (int i = 0; i < threadVector.size(); i++) {
01003       BirThread thread = threadVector.elementAt(i);
01004       println("assert(run " + thread.getName() + "() == " +
01005               (1 + thread.getId()) + ");");
01006     }
01007     indentLevel--;
01008     println("}");
01009 
01010     // Afterwards, just hang out asserting that there is no limit
01011     // exception (i.e., a limit exception will cause a counterexmaple).
01012     // This statement is not emitted in the PROMELA if
01013     // the user wants to do a resource bounded check.
01014     if (!options.getResourceBounded())
01015       println("assert(! limit_exception);");
01016     indentLevel = 0;
01017     println("}");
01018 
01019   }  
01020   /**
01021    * Test if the out transitions of a location model an 'if' branch,
01022    * i.e., there are exactly two transitions, and one guard is
01023    * the negation of the other.
01024    */
01025 
01026   boolean isIfBranch(TransVector outTrans) {
01027     if (outTrans.size() != 2)
01028       return false;
01029     Expr guard1 = outTrans.elementAt(0).getGuard();
01030     Expr guard2 = outTrans.elementAt(1).getGuard();
01031     if (guard1 == null || guard2 == null)
01032       return false;
01033     if ((guard1 instanceof NotExpr) &&
01034         ((NotExpr) guard1).getOp().equals(guard2))
01035       return true;
01036     if ((guard2 instanceof NotExpr) &&
01037         ((NotExpr) guard2).getOp().equals(guard1))
01038       return true;
01039     return false;
01040   }  
01041     public boolean isVerbose() { return isVerbose; }
01042   /**
01043    * Get PROMELA label for BIR location.
01044    * <p>
01045    * If location has no out transitions (i.e., is end location of thread),
01046    * make the label an end-label (does not count for deadlock).
01047    */
01048 
01049   static String locLabel(Location loc) {
01050     if (loc.getOutTrans().size() == 0)
01051       return "endloc_" + loc.getId();
01052     else
01053       return "loc_" + loc.getId();
01054   }  
01055   /**
01056    * Get PROMELA label of location encountered during coarsening.
01057    * <p>
01058    * Since we may encounter the same location along several
01059    * collapsed paths, we must have a unique label for each.
01060    */
01061 
01062   static String locLabel(Location loc1, int branch, Location loc2) {
01063     return "loc_" + loc1.getId() + "_" + branch + "_" + loc2.getId();
01064   }  
01065   /**
01066    * Test if an expression is statically nonnegative (either a constant
01067    * or in a range type that is nonnegative).
01068    */
01069 
01070   boolean nonNegative(Expr expr) {
01071     if (expr instanceof ConstExpr)
01072       return ((ConstExpr) expr).getValue() >= 0;
01073     if (expr.getType() instanceof Range) {
01074       Range type = (Range) expr.getType();
01075       return (type.getFromVal().getValue() >= 0);
01076     }
01077     return false;
01078   }  
01079   static int parseInt(String s) {
01080     try {
01081       int result = Integer.parseInt(s);
01082       return result;
01083     } catch (NumberFormatException e) {
01084       throw new RuntimeException("Integer didn't parse: " + s);
01085     }
01086   }  
01087 
01088   /**
01089     * Parse the output of 'pan' and interpret it as either a violation
01090     * (sequence of transformations) or as a error-free analysis.
01091     * <p>
01092     * @return a vector of transformations (if there was an error found), or null (otherwise)
01093     */
01094 
01095   public BirTrace parseOutput() {
01096     return new BirTrace(system, getCounterExample());
01097   }          
01098   /**
01099    * Generate the predicate definitions.
01100    * <p>
01101    * Note that these are at the bottom so they can refer to
01102    * any variable or code location.
01103    */
01104 
01105   void predicates() {
01106     println("/* Predicates */");
01107     Vector preds = system.getPredicates();
01108     for (int i = 0; i < preds.size(); i++) {
01109       Expr pred = (Expr) preds.elementAt(i);
01110       String name = system.predicateName(pred);
01111       inDefine = true;
01112       println("#define " + name + " ");
01113       indentLevel = 1;
01114       // Print the PROMELA expression
01115       printGuard(applyTo(pred).getCase(normal));
01116       inDefine = false;
01117       println("");
01118       indentLevel = 0;
01119     }
01120     println("");
01121   }  
01122   /**
01123    * Support for printing things nicely indented.
01124    */
01125 
01126   void print(String s) {
01127     if (newLine) { // at start of line---indent
01128       for (int i = 0; i < indentLevel; i++)
01129         out.print("   ");
01130       newLine = false;
01131     }
01132     out.print(s);
01133   }  
01134   /**
01135    * Print a case tree as an expression to represent a guard.
01136    * <p>
01137    * We take advantage of the short-curcuit evaluation in PROMELA
01138    * to express a case tree as one large boolean expression.
01139    * A case node:
01140    * <pre>
01141    * (cond1 => expr1, cond2 => expr2, ...)
01142    * </pre>
01143    * is translated as
01144    * <pre>
01145    * ((cond1 && expr1) || (cond2 && expr2) || ... )
01146    * </pre>
01147    * We consider traps as always enabled, so they evaluate to
01148    * the constant TRAP (#defined to be 1).
01149    */
01150 
01151   void printGuard(TreeNode tree) {
01152     if (tree instanceof ExprNode) {
01153       ExprNode node = (ExprNode) tree;
01154       print(node.expr1);
01155     } else if (tree instanceof TrapNode) {
01156       TrapNode node = (TrapNode) tree;
01157       print("TRAP");
01158     } else {
01159       CaseNode node = (CaseNode) tree;
01160       print("(  ");
01161       for (int i = 0; i < node.size(); i++) {
01162         Case c = node.elementAt(i);
01163         indentLevel++;
01164         println("(  " + c.cond);
01165         print("&& ");
01166         indentLevel++;
01167         printGuard(c.node);
01168         indentLevel--;
01169         print(" )");
01170         indentLevel--;
01171         if (i < node.size() - 1) {
01172           println();
01173           print("|| ");
01174         }
01175       }
01176       println(" )");
01177     }
01178   }  
01179   void println() {
01180     if (inDefine)
01181       out.print("\\"); // must use line continuation in #define
01182     out.println();
01183     newLine = true;
01184   }  
01185   void println(String s) {
01186     print(s);
01187     println();
01188   }  
01189   /**
01190    * Print a case tree as a statement.
01191    * <p>
01192    * A case node:
01193    * <pre>
01194    * (cond1 => expr1, cond2 => expr2, ..., condN => exprN)
01195    * </pre>
01196    * is translated as
01197    * <pre>
01198    * if
01199    * :: cond1 -> expr1
01200    * :: cond2 -> expr2
01201    * ...
01202    * else -> exprN
01203    * fi
01204    * </pre>
01205    */
01206 
01207   void printStatement(TreeNode tree) {
01208     if (tree instanceof ExprNode) {
01209       ExprNode node = (ExprNode) tree;
01210       println(node.expr1);
01211     } else if (tree instanceof TrapNode) {
01212       TrapNode node = (TrapNode) tree;
01213       print("   printf(\"BIR: " + actionId() + " " + node.desc +
01214             "\\n\"); ");
01215       // If fatal trap, flag error, else just branch to end label
01216       if (node.fatal)
01217         println("assert(0);");
01218       else
01219         println("limit_exception = 1; goto endtrap;");
01220     } else {
01221       CaseNode node = (CaseNode) tree;
01222       println("if ");
01223       for (int i = 0; i < node.size(); i++) {
01224         Case c = node.elementAt(i);
01225         // Use "else" for last alternative
01226         String cond = (i < node.size() - 1) ? c.cond : "else";
01227         println(":: " + cond + " -> ");
01228         indentLevel++;
01229         printStatement(c.node);
01230         indentLevel--;
01231       }
01232       println("fi; ");
01233     }
01234   }  
01235   /**
01236    * Generate the #include for the never claim
01237    */
01238 
01239   void property() {
01240     if (options.ApplyNeverClaim)
01241       println("#include \"" + system.getName() + ".nvr\"");
01242   }  
01243   /**
01244    * Return the refIndex of a state variable (it's index in the
01245    * universal reference type refAny).
01246    */
01247 
01248   int refIndex(StateVar target) {
01249     return 1 + system.refAnyType().getTargets().indexOf(target);
01250   }  
01251   /**
01252    * Reset any variables that have become dead between locations.
01253    * <p>
01254    * In particular, we reset a variable if it was live at fromLoc and
01255    * is not live at toLoc.
01256    */
01257 
01258   void resetDeadVariables(Location fromLoc, Location toLoc) {
01259     StateVarVector liveBefore = fromLoc.getLiveVars();
01260     StateVarVector liveAfter = toLoc.getLiveVars();
01261     if (liveBefore != null && liveAfter != null)
01262       for (int i = 0; i < liveBefore.size(); i++)
01263         if (liveAfter.indexOf(liveBefore.elementAt(i)) == -1) {
01264           // var was live, now is not---reset it
01265           StateVar var = liveBefore.elementAt(i);
01266           var.apply(this);
01267           print(getSimpleResult() + " = ");
01268           var.getInitVal().apply(this);
01269           println(getSimpleResult() + ";");
01270         }
01271   }  
01272   /**
01273    * Run translator: generate header, definitions, state variables,
01274    * transitions (processes), predicates, and the LTL property.
01275    */
01276 
01277   SpinTrans run() {
01278     header();
01279     definitions();
01280     stateVariables();
01281     transitions();
01282     predicates();
01283     property();
01284     return this;
01285   }  
01286   /**
01287     * Run SPIN to generate an analyzer (pan), and then run the analyzer.
01288     * <p>
01289     * The translator must have been executed for the transition system
01290     * (the file NAME.prom must exist and contain the translated PROMELA).
01291     * @return the output of 'pan' (as a String)
01292     */
01293 
01294 public String runSpin() {
01295     return check();
01296 /*
01297     String systemName = system.getName();
01298     try {
01299       if (options.ApplyNeverClaim) {
01300     // Translate LTL into never claim
01301     String ltlFilename = systemName + ".ltl";
01302     File ltlFile = new File(ltlFilename);
01303     if (!ltlFile.exists())
01304       throw new RuntimeException( "Cannot find LTL file: " +
01305                       ltlFile.getAbsolutePath());
01306     String neverClaim =
01307       execAndWait("spin -F " + ltlFilename, true);
01308     PrintWriter pw =
01309       new PrintWriter(new FileWriter(systemName + ".nvr"));
01310     pw.print(neverClaim);
01311     pw.flush();
01312     pw.close();
01313       }
01314       String promFilename =
01315     isWindows ? systemName : systemName + ".prom";
01316       File sourceFile = new File(promFilename);
01317       if (!sourceFile.exists())
01318     throw new RuntimeException(
01319                    "Cannot find SPIN source file: " +
01320                    sourceFile.getAbsolutePath());
01321 
01322       { // Remove any old trail & pan.exe files
01323     new File(isWindows ? systemName + ".prom.trail" :
01324          systemName + ".tra").delete();
01325     new File("pan.exe").delete();
01326       }
01327       //      { // Execute cpp
01328       //        execAndWait("cpp -P " + promFilename + " tmp", true);
01329       //        File promFile = new File(promFilename);
01330       //      if (!promFile.delete() ||
01331       //            !new File("tmp").renameTo(promFile))
01332       //          throw new RuntimeException(
01333       //            "Could not rename tmp to " + promFilename);
01334       //        }
01335       execAndWait("spin -a " + promFilename, true);
01336       execAndWait(ccName + " " + options.compileOptions2() + " -o pan.exe pan.c",
01337           true);
01338       ranVerifier = true;
01339       // Return output of 'pan'
01340       return panOutput = execAndWait(isWindows ?
01341                      "pan.exe -ttra " + options.panOptions() :
01342                      "pan.exe " + options.panOptions(), true);
01343     } catch (Exception e) {
01344       throw new RuntimeException(
01345                  "Could not produce SPIN file (" + e + ")");
01346     }
01347 */
01348 }            
01349     public void setVerbose(boolean v) { isVerbose = v; }
01350   /**
01351    * Build a trivial case tree with one ExprNode holding the string.
01352    */
01353 
01354   TreeNode specialize(String expr) {
01355     return new CaseNode(new Case(normal, new ExprNode(expr)));
01356   }  
01357   /**
01358    * Declare the state variables.
01359    */
01360 
01361   void stateVariables() {
01362     // limit_exeption is set on a BIR limit exception
01363     // _i_ is used as a temporary in the _allocate macro
01364     println("bool limit_exception = 0;");
01365     println("byte _i_ = 0;");
01366 
01367     // For each (non-main) thread, declare channel to block on for starting
01368     // the thread and a boolean indicating whether the thread is active
01369     ThreadVector threads = system.getThreads();
01370     for (int i = 0; i < threads.size(); i++) {
01371       if (! threads.elementAt(i).isMain()) {
01372         println("chan _threadStart_" + i + " = [1] of { bit };");
01373         println("bit _threadActive_" + i + ";");
01374       }
01375     }
01376     println();
01377 
01378     // For each state variable, declare it with its type.
01379     StateVarVector stateVarVector = system.getStateVars();
01380     for (int i = 0; i < stateVarVector.size(); i++) {
01381       StateVar var = stateVarVector.elementAt(i);
01382       var.getType().apply(namer, null);
01383       String refIndex = "   ";
01384       // Print ref index for composites to help reader
01385       if (var.getType().isKind(RECORD | ARRAY | COLLECTION))
01386         refIndex += "/*  ref index = " + refIndex(var) + " */";
01387       println((String) namer.getResult() + " " + var.getName() +
01388               ";" + refIndex);
01389     }
01390     println();
01391   }  
01392   /**
01393    * Generate the threads.
01394    * <p>
01395    * Each thread is a PROMELA proctype with a label for each
01396    * (visible) location and an if-case for each transformation
01397    * out of that location.
01398    */
01399 
01400   void transitions() {
01401     ThreadVector threadVector = system.getThreads();
01402     for (int i = 0; i < threadVector.size(); i++) {
01403       BirThread thread = threadVector.elementAt(i);
01404       println("proctype " + thread.getName() + "() { ");
01405       // Temp for thread
01406       println("  int _temp_;");
01407       // For non-main threads, insert code to block until started
01408       if (! thread.isMain())
01409         println("endtop_" + i + ":\n   _beginThread_" + i + ";");
01410 
01411       // Generate label/trans for each location
01412       LocVector threadLocVector = thread.getLocations();
01413       for (int j = 0; j < threadLocVector.size(); j++)
01414         translateLocation(threadLocVector.elementAt(j));
01415 
01416       // Special end location (accepting)
01417       println("endtrap:");
01418       // For resource bounded searches we cause the model checker to go into
01419       // an infinite loop when a limit resource execption has been raised.
01420       // Otherwise we simply denote a Promela end state (with a 0).
01421       if (options.getResourceBounded()) {
01422         println("   if");
01423         println("   :: limit_exception -> goto endtrap;");
01424         println("   :: !limit_exception -> 0;");
01425         println("   fi;");
01426       } else {
01427         println(" 0;");
01428       }
01429       println("}");
01430       println();
01431     }
01432     initBlock();
01433   }  
01434   /**
01435    * Generate PROMELA source representing a transition system.
01436    * <p>
01437    * As above, but with default options.
01438    * @param system the transition system
01439    * @return the SpinTrans control object
01440    */
01441 
01442   public static SpinTrans translate(TransSystem system) {
01443     return translate(system, null);
01444   }  
01445   /**
01446    * Generate PROMELA source representing a transition system.
01447    * <p>
01448    * The PROMELA is written to a file NAME.prom where NAME
01449    * is the name of the transition system.  A set of
01450    * options can be provided in a SpinOptions object.
01451    * @param system the transition system
01452    * @param options the SPIN verifier options
01453    * @return the SpinTrans control object
01454    */
01455 
01456   public static SpinTrans translate(TransSystem system, SpinOptions options) {
01457     return translate(system, options, ".");
01458   }      
01459   /**
01460    * Generate PROMELA source representing a transition system.
01461    * <p>
01462    * As above, but the PROMELA is written to the PrintWriter provided.
01463    * @param system the transition system
01464    * @param out the PrintWriter to write the PROMELA to
01465    * @return the SpinTrans control object
01466    */
01467 
01468 public static SpinTrans translate(TransSystem system, SpinOptions options, PrintWriter out) {
01469     return (new SpinTrans(system, options, out)).run();
01470 }
01471 public static SpinTrans translate(TransSystem system, SpinOptions options, String outputDir) {
01472     try {
01473         if (outputDir == null || "".equals(outputDir)) outputDir = "."+File.separator; else
01474             if (!outputDir.endsWith(File.separator)) outputDir = outputDir + File.separator;
01475 
01476         // File sourceFile = new File(isWindows ? system.getName() : system.getName() + ".prom");
01477         File sourceFile = new File(system.getName() + ".prom");
01478         FileOutputStream streamOut = new FileOutputStream(sourceFile);
01479         PrintWriter writerOut = new PrintWriter(streamOut);
01480         SpinTrans result = translate(system, options, writerOut);
01481         result.outputDir = outputDir;
01482         writerOut.close();
01483         return result;
01484     } catch (IOException e) {
01485         throw new RuntimeException("Could not produce SPIN file: " + e);
01486     }
01487 }  
01488   /**
01489    * Translate binary op
01490    */
01491 
01492   void translateBinaryOp(Expr e1, Expr e2, String op) {
01493     TreeNode e1Tree = applyTo(e1);
01494     TreeNode e2Tree = applyTo(e2);
01495     // Compose argument trees and then update each leaf
01496     TreeNode result = e1Tree.compose(e2Tree, null);
01497     Vector leaves = result.getLeaves(new Vector());
01498     for (int i = 0; i < leaves.size(); i++) {
01499       ExprNode leaf = (ExprNode) leaves.elementAt(i);
01500       leaf.update("(" + leaf.expr1 + op + leaf.expr2 + ")");
01501     }
01502     setResult(result);
01503   }  
01504   /**
01505    * Generate PROMELA for visible BIR location.
01506    */
01507 
01508   public void translateLocation(Location loc) {
01509     if (! loc.isVisible())
01510       return;
01511     println(locLabel(loc) + ":");
01512     indentLevel++;
01513     TransVector outTrans = loc.getOutTrans();
01514     // Don't both with 'if' when there's only one transition
01515     if (outTrans.size() == 1)
01516       translateSequence(outTrans.elementAt(0), 0);
01517     else if (outTrans.size() == 0)
01518       println("0;");
01519     else {
01520       println("if");
01521       for (int i = 0; i < outTrans.size(); i++) {
01522         print(":: ");
01523         indentLevel++;
01524         translateSequence(outTrans.elementAt(i), i);
01525         indentLevel--;
01526       }
01527       println("fi;");
01528     }
01529     indentLevel--;
01530   }  
01531   /**
01532    * Translate a sequence of transformations starting with the given one,
01533    * continuing until we hit a visible transformation.
01534    * <p>
01535    * The locSet param keeps track of the locations we've encountered
01536    * in this branch (i.e., this atomic block).  If we transition to
01537    * an invisible location in this set, we just generate a goto to the local
01538    * label for that location, otherwise we recursively call translateSeq
01539    * to continue following the sequence.
01540    * <p>
01541    * If we transition to a visible location, we always generate a goto
01542    * to the top-level location label for that location.
01543    * <p>
01544    * The guard of the transformation may be supressed by setting
01545    * param supressGuard.  This is used when an 'if' branch pattern
01546    * is detected (two transitions, one on expression E, the other
01547    * on expression !E).
01548    */
01549 
01550   void translateSeq(LocVector locSet, Transformation trans,
01551                     int branch, boolean supressGuard) {
01552     // Generate code for one transformation (possibly without the guard)
01553     translateTrans(trans, supressGuard);
01554 
01555     // If the target location is visible, reset the dead variables
01556     // and branch to the code for that location
01557     if (trans.getToLoc().isVisible()) {
01558       resetDeadVariables(locSet.firstElement(), trans.getToLoc());
01559       println("goto " + locLabel(trans.getToLoc()) + ";");
01560     }
01561 
01562     // If the target location is invisible, but we've already
01563     // generated code for it in this atomic block, branch to
01564     // the local label for that location.
01565     else if (locSet.contains(trans.getToLoc()))
01566       println("goto " + locLabel(locSet.firstElement(), branch,
01567                                  trans.getToLoc()) + ";");
01568 
01569     // Otherwise, continue following the sequence
01570     else {
01571       locSet.addElement(trans.getToLoc());
01572 
01573       // If there's more than one way into this location, we
01574       // may branch back to it from within the atomic block, so
01575       // generate a local label for the location.
01576       if (trans.getToLoc().getInTrans().size() > 1)
01577         println( locLabel(locSet.firstElement(), branch,
01578                           trans.getToLoc()) + ":");
01579       TransVector successors = trans.getToLoc().getOutTrans();
01580       // For one sucessor, don't bother with an 'if'
01581       if (successors.size() == 1)
01582         translateSeq(locSet, successors.elementAt(0), branch,
01583                      false);
01584       // For 'if' branches, use a PROMELA 'else' for efficiency
01585       else if (isIfBranch(successors)) {
01586         println("if");
01587         print(":: ");
01588         indentLevel++;
01589         translateSeq(locSet, successors.elementAt(0), branch,
01590                      false);
01591         indentLevel--;
01592         print(":: else -> ");
01593         indentLevel++;
01594         translateSeq(locSet, successors.elementAt(1), branch, true);
01595         indentLevel--;
01596         println("fi;");
01597       } else { // general case
01598         println("if");
01599         for (int i = 0; i < successors.size(); i++) {
01600           print(":: ");
01601           indentLevel++;
01602           translateSeq(locSet, successors.elementAt(i),
01603                        branch, false);
01604           indentLevel--;
01605         }
01606         println("fi;");
01607       }
01608     }
01609   }  
01610   /**
01611    * Translate a sequence of transformations, continuing until
01612    * we hit a visible transformation.  Make the sequence
01613    * atomic.
01614    * <p>
01615    * The 'branch' parameter is used to generate location
01616    * labels that are local to this atomic block.
01617    */
01618 
01619   void translateSequence(Transformation trans, int branch) {
01620     Location startLoc = trans.getFromLoc();
01621     println("atomic { ");
01622     indentLevel++;
01623     translateSeq(new LocVector(startLoc), trans, branch, false);
01624     indentLevel--;
01625     println("}");
01626   }  
01627   /**
01628    * Generate code for a single transformation (possibly suppressing
01629    * the code for the guard expression).
01630    */
01631 
01632   void translateTrans(Transformation trans, boolean supressGuard) {
01633     // Set this trans as the context (for action ID generation)
01634     currentTrans = trans;
01635     actionNum = 0;
01636     // Translate and print the guard if present and not supressed
01637     if (! supressGuard && guardPresent(trans)) {
01638       printGuard(applyTo(trans.getGuard()).getCase(normal));
01639       println("-> ");
01640     }
01641     ActionVector actions = trans.getActions();
01642     // Translate each action
01643     for (int i = 0; i < actions.size(); i++) {
01644       actionNum = i + 1;
01645       println("printf(\"BIR: " + actionId() + " OK\\n\"); ");
01646       actions.elementAt(i).apply(this);
01647     }
01648     // If we used _temp_, reset it
01649     if (resetTemp) {
01650       println("_temp_ = 0;");
01651       resetTemp = false;
01652     }
01653   }  
01654   /**
01655    * Translate unary op
01656    */
01657 
01658   void translateUnaryOp(Expr e, String op) {
01659     TreeNode result = applyTo(e);
01660     Vector leaves = result.getLeaves(new Vector());
01661     // Translate argument and then apply to each leaf
01662     for (int i = 0; i < leaves.size(); i++) {
01663       ExprNode leaf = (ExprNode) leaves.elementAt(i);
01664       leaf.update("(" + op + leaf.expr1 + ")");
01665     }
01666     setResult(result);
01667   }  
01668 }

Generated at Thu Feb 7 06:55:31 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001