From febcc0188af795b5deba0808cbe9c760fbcf1fb0 Mon Sep 17 00:00:00 2001
From: tempiaa <tempiaa@eurecom.fr>
Date: Wed, 22 Apr 2020 12:10:22 +0200
Subject: [PATCH] Added safety pragmas on states. Generalized liveness check as
 a safety check

---
 .../AvatarExpressionSolver.java               |  78 +++++++
 .../modelchecker/AvatarModelChecker.java      | 200 +++++++-----------
 .../modelchecker/SafetyProperty.java          |  22 +-
 .../modelchecker/SpecificationState.java      |   4 +-
 .../ui/window/JDialogAvatarModelChecker.java  |  12 +-
 5 files changed, 180 insertions(+), 136 deletions(-)

diff --git a/src/main/java/avatartranslator/AvatarExpressionSolver.java b/src/main/java/avatartranslator/AvatarExpressionSolver.java
index b50dc67d12..bc97b54383 100644
--- a/src/main/java/avatartranslator/AvatarExpressionSolver.java
+++ b/src/main/java/avatartranslator/AvatarExpressionSolver.java
@@ -247,6 +247,84 @@ public class AvatarExpressionSolver {
         return returnVal;
     }
     
+    public boolean buildExpression() {
+        boolean returnVal;
+        
+        removeUselessBrackets();
+
+        if (!expression.matches("^.+[\\+\\-<>=&\\*/].*$")) {
+            // leaf
+            isLeaf = true;
+            if (expression.equals("true")) {
+                intValue = 1;
+                isImmediateValue = IMMEDIATE_INT;
+                returnVal = true;
+            } else if (expression.equals("false")) {
+                intValue = 0;
+                isImmediateValue = IMMEDIATE_INT;
+                returnVal = true;
+            } else if (expression.matches("-?\\d+")) {
+                intValue = Integer.parseInt(expression);
+                isImmediateValue = IMMEDIATE_INT;
+                returnVal = true;
+            } else {
+                returnVal = false;
+            }
+            //System.out.println("Variable " + expression + "\n");
+            return returnVal;
+        }
+        
+        isLeaf = false;
+        
+        if (expression.startsWith("not(")) {
+            //not bracket must be closed in the last char
+            int closingIndex = getClosingBracket(4);
+            
+            if (closingIndex == -1) {
+                return false;
+            }
+            if (closingIndex == expression.length() - 1) {
+              //not(expression)
+                isNot = true;
+                expression = expression.substring(4, expression.length() - 1).trim();
+            }
+        }
+        
+        if (expression.startsWith("-(")) {
+            //not bracket must be closed in the last char
+            int closingIndex = getClosingBracket(4);
+            
+            if (closingIndex == -1) {
+                return false;
+            }
+            if (closingIndex == expression.length() - 1) {
+              //not(expression)
+                isNot = true;
+                expression = expression.substring(2, expression.length() - 1).trim();
+            }
+        }
+        
+        int index = getOperatorIndex();
+        
+        if (index == -1) {
+            return false;
+        }
+        
+        operator = expression.charAt(index);
+        
+        //split and recur
+        String leftExpression = expression.substring(0, index).strip();
+        String rightExpression = expression.substring(index + 1, expression.length()).strip();
+        
+        left = new AvatarExpressionSolver(leftExpression);
+        right = new AvatarExpressionSolver(rightExpression);
+        //System.out.println("Expression " + expression + " ; " + leftExpression + " ; " + rightExpression + "\n");  
+        returnVal = left.buildExpression();
+        returnVal &= right.buildExpression();
+        
+        return returnVal;
+    }
+    
     private void replaceOperators() {
         expression = expression.replaceAll("\\|\\|", "\\|").trim();
         expression = expression.replaceAll("&&", "&").trim();
diff --git a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
index 13a480f118..6895b35dbe 100644
--- a/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
+++ b/src/main/java/avatartranslator/modelchecker/AvatarModelChecker.java
@@ -96,14 +96,11 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     private boolean deadlockStop;
     private int nbOfDeadlocks;
 
-    // Liveness
-    private boolean propertyDone;
-    private boolean studyLiveness;
-    private ArrayList<SpecificationLiveness> livenesses;
-    private SpecificationLiveness livenessInfo;
-    
-    // Safety
+    // Safety and Liveness
     private boolean studySafety;
+    private boolean studyLiveness;
+    private boolean propertyDone;
+    private ArrayList<SafetyProperty> livenesses;
     private ArrayList<SafetyProperty> safeties;
     private SafetyProperty safety;
     
@@ -205,14 +202,14 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     }
 
     public int setLivenessOfSelected() {
-        livenesses = new ArrayList<SpecificationLiveness>();
+        livenesses = new ArrayList<SafetyProperty>();
         for (AvatarBlock block : spec.getListOfBlocks()) {
             for (AvatarStateMachineElement elt : block.getStateMachine().getListOfElements()) {
                 //TraceManager.addDev("null elt in state machine of block=" + block.getName());
                 //if (elt.canBeVerified() && elt.isChecked()) {
                 if (elt.isChecked()) {
-                    SpecificationLiveness sl = new SpecificationLiveness(elt, block);
-                    livenesses.add(sl);
+                    SafetyProperty sp = new SafetyProperty(block, elt);
+                    livenesses.add(sp);
                 }
             }
         }
@@ -221,12 +218,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     }
     
     public int setLivenessOfAllStates() {        
-        livenesses = new ArrayList<SpecificationLiveness>();
+        livenesses = new ArrayList<SafetyProperty>();
         for (AvatarBlock block : spec.getListOfBlocks()) {
             for (AvatarStateMachineElement elt : block.getStateMachine().getListOfElements()) {
                 if (((elt instanceof AvatarStateElement) && (elt.canBeVerified())) || (elt.isCheckable())) {
-                    SpecificationLiveness sl = new SpecificationLiveness(elt, block);
-                    livenesses.add(sl);
+                    SafetyProperty sp = new SafetyProperty(block, elt);
+                    livenesses.add(sp);
                 }
             }
         }
@@ -234,7 +231,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         return livenesses.size();
     }
     
-    public ArrayList<SpecificationLiveness> getLivenesses() {
+    public ArrayList<SafetyProperty> getLivenesses() {
         return livenesses;
     }
 
@@ -354,15 +351,15 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         studyReachability = false;
 
         if (studyL) {
-            studyLiveness = true;
-            for (SpecificationLiveness sl : livenesses) {
-                livenessInfo = sl;
+            studySafety = true;
+            for (SafetyProperty sp : livenesses) {
+                safety = sp;
                 startModelChecking();
                 propertyDone = false;
                 stoppedConditionReached = false;
                 deadlocks += nbOfDeadlocks;
             }
-            studyLiveness = false;
+            studySafety = false;
         }
         
         if (studyS) {
@@ -423,7 +420,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         spec.makeFullStates();
 
         if (ignoreEmptyTransitions) {
-            spec.removeEmptyTransitions((nbOfRemainingReachabilities == 0) || studyLiveness || studySafety);
+            spec.removeEmptyTransitions((nbOfRemainingReachabilities == 0) || studySafety);
         }
         
         initExpressionSolvers();
@@ -464,21 +461,30 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         SpecificationState initialState = new SpecificationState();
         //initialState.setInit(spec, ignoreEmptyTransitions);
         initialState.setInit(spec, false);
+        
         for (AvatarBlock block : spec.getListOfBlocks()) {
-            checkElement(block.getStateMachine().getStartState(), initialState);
+            AvatarStateMachineElement asme = block.getStateMachine().getStartState();
+            checkElement(asme, initialState);
+            if (studySafety && safety.propertyType == SafetyProperty.BLOCK_STATE && asme == safety.state) {
+                initialState.property = true;
+                break;
+            }
+        }
+        if (studySafety && safety.propertyType == SafetyProperty.BLOCK_STATE) {
+                if (safety.safetyType == SafetyProperty.ALLTRACES_ALLSTATES || safety.safetyType == SafetyProperty.ONETRACE_ALLSTATES) {
+                    initialState.property = !initialState.property;
+                }
         }
+        
         //initialState.id = 0//getStateID();
         if (ignoreEmptyTransitions) {
             handleNonEmptyUniqueTransition(initialState);
         }
         
-        if (studyLiveness && initialState.property == true) {
-            //property on initial states
-            return;
-        }
-        
         if (studySafety) {
-            initialState.property = evaluateSafetyProperty(initialState, false);
+            if (safety.propertyType == SafetyProperty.BOOL_EXPR) {
+                initialState.property = evaluateSafetyOfProperty(initialState, false);
+            }
             actionOnProperty(initialState, 0, null, null);
         } else {
             pendingStates.add(initialState);
@@ -856,13 +862,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             executeTransition(_ss, newState, tr);
             String action = tr.infoForGraph;
             
-            if (studyLiveness) {
-                //set property value for state
-                setLivenessofState(newState, tr, _ss.property);
-            }
-            
             if (studySafety) {
-                newState.property = evaluateSafetyProperty(newState, _ss.property);
+                if (safety.propertyType == SafetyProperty.BLOCK_STATE) {
+                    newState.property = evaluateSafetyOfState(tr, _ss.property);
+                } else {
+                    newState.property = evaluateSafetyOfProperty(newState, _ss.property);
+                }
             }
 
             // Remove empty transitions if applicable
@@ -906,7 +911,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 link.destinationState = newState;
                 newState.distance = _ss.distance + 1;
                 
-                if (!studyLiveness && !studySafety) {
+                if (!studySafety) {
                     pendingStates.add(newState);
                 } else {
                     actionOnProperty(newState, i, similar, _ss);
@@ -959,13 +964,12 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             newState.increaseClockOfBlocksExcept(st);
             executeTransition(previousState, newState, st);
             
-            if (studyLiveness) {
-                //set property value for state
-                setLivenessofState(newState, st, previousState.property);
-            }
-            
             if (studySafety) {
-                newState.property = evaluateSafetyProperty(newState, previousState.property);
+                if (safety.propertyType == SafetyProperty.BLOCK_STATE) {
+                    newState.property = evaluateSafetyOfState(st, previousState.property);
+                } else {
+                    newState.property = evaluateSafetyOfProperty(newState, previousState.property);
+                }
             }
             
             if (ignoreEmptyTransitions) {
@@ -1073,7 +1077,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
                 } else {
                     link.destinationState = newState;
                     newState.distance = _ss.distance + 1;
-                    if (!studyLiveness && !studySafety) {
+                    if (!studySafety) {
                         pendingStates.add(newState);
                     } else {
                         actionOnProperty(newState, 0, similar, _ss);
@@ -1346,13 +1350,6 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         for (int i = 0; i < _st.transitions.length; i++) {
             ase = getNextState(_st.transitions[i], _newState, 10);
             if (ase != null) {
-                
-//                if (studyLiveness) {
-//                    if (livenessInfo.ref1 == ase) {
-//                        TraceManager.addDev("Liveness found on a path");
-//                        _newState.liveness = true;
-//                    }
-//                }
                 checkElement(ase, _newState);
                 int index = _st.blocks[i].getStateMachine().getIndexOfState(ase);
                 if (index > -1) {
@@ -1385,23 +1382,6 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         }
         return getNextState(e, _newState, maxNbOfIterations);
     }
-    
-    private AvatarStateMachineElement getNextStateLivenessCheck(AvatarStateMachineElement e, int maxNbOfIterations) {
-        //Returns an element if there is a liveness checked element else it returns a state
-        e = e.getNext(0);
-        if (e == livenessInfo.ref1) {
-            //liveness element found
-            return e;
-        }
-        if (e instanceof AvatarStateElement) {
-            return (AvatarStateElement) e;
-        }
-        maxNbOfIterations--;
-        if (maxNbOfIterations == 0) {
-            return null;
-        }
-        return getNextStateLivenessCheck(e, maxNbOfIterations);
-    }
 
     // Execute the actions of a transition, and correspondingly impact the variables of the
     // block executing the transition
@@ -1543,21 +1523,16 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
 
         //      TraceManager.addDev("Handling Empty transition of previous=" + _ase.getName());
         
-        if (studyLiveness) {
-            if (_ase == livenessInfo.ref1) {
-                _ss.property = true;
+        if (studySafety && safety.propertyType == SafetyProperty.BLOCK_STATE) {
+            boolean result = false;
+            if (safety.state == _ase) {
+                result = true;
             }
+            if (safety.safetyType == SafetyProperty.ALLTRACES_ALLSTATES || safety.safetyType == SafetyProperty.ONETRACE_ALLSTATES) {
+                result = !result;
+            }
+            _ss.property |= result;
         }
-        
-//        if (studySafety) {
-//            if (safety.propertyType == SafetyProperty.BLOCK_STATE && safety.state == _ase) {
-//                if (safety.safetyType == SafetyProperty.ALLTRACES_ALLSTATES || safety.safetyType == SafetyProperty.ONETRACE_ALLSTATES) {
-//                    _ss.property = false;
-//                } else {
-//                    _ss.property = true;
-//                }
-//            }
-//        }
 
         if (_ase.getNexts().size() != 1) {
             return _ase;
@@ -1614,33 +1589,31 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
 
     }
     
-    private boolean setLivenessofState(SpecificationState newState, SpecificationTransition tr, boolean precLiveness) {
+    
+    private boolean evaluateSafetyOfState(SpecificationTransition tr, boolean precLiveness) {
         AvatarStateMachineElement asme;
-        boolean found = false;
+        boolean result = false;
         
-        // Find if newState carries the transition to a liveness check state
+        // Find if newState carries the transition to a safety check stateElement
         for (int i = 0; i < tr.transitions.length; i++) {
-            asme = getNextStateLivenessCheck(tr.transitions[i], 10);
-            if (asme != null) {
-                if (livenessInfo.ref1 == asme) {
-                    newState.property = true;
-                    found = true;
-                }
+            //asme = getNextStateLivenessCheck(tr.transitions[i], 10);
+            asme = tr.transitions[i].getNext(0);
+            if (asme == safety.state) {
+                result = true;
+                break;
             }
         }
-        if (!found) {
-            newState.property = precLiveness;
+        
+        if (safety.safetyType == SafetyProperty.ALLTRACES_ALLSTATES || safety.safetyType == SafetyProperty.ONETRACE_ALLSTATES) {
+            result = !result;
         }
-        return found || precLiveness;
+        
+        return result || precLiveness;
     }
 
     
-    private boolean evaluateSafetyProperty(SpecificationState newState, boolean precProperty) {
-        boolean result = safety.getResult(newState);
-        
-        //if (result != evaluateBoolExpression(safety.getP(), safety.block, newState.blocks[safety.blockIndex])) {
-            //System.out.println("Different");
-        //}
+    private boolean evaluateSafetyOfProperty(SpecificationState newState, boolean precProperty) {
+        boolean result = safety.getSolverResult(newState);
         
         // the value to be associated to the state property depends on the type of property to be checked
         // A[] -> !result; A<> -> result; E[] -> !result; E<> -> result
@@ -1652,27 +1625,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     }
     
     private void actionOnProperty(SpecificationState newState, int i, SpecificationState similar, SpecificationState _ss) {
-        if (studyLiveness) {
-            if (similar == null && !newState.property) { 
-                if (i == 0) {
-                    //Priority for parallel DFS on the first transition
-                    pendingStates.add(0, newState);
-                } else {
-                    //Not priority for parallel BFS on the other transitions
-                    pendingStates.add(newState);
-                }
-            } else if (similar != null && newState.property == false) {
-                if (similar.property) {
-                    //found a branch with false liveness
-                    propertyDone = true;
-                    livenessInfo.result = false;
-                } else if (stateIsReachableFromState(similar, _ss)) {
-                    //found a loop with false liveness
-                    propertyDone = true;
-                    livenessInfo.result = false;
-                }
-            }
-        } else if (studySafety) {
+        if (studySafety) {
             if (safety.safetyType == SafetyProperty.ALLTRACES_ALLSTATES) {
                 if (newState.property) {
                     propertyDone = true;
@@ -1724,12 +1677,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
     }
     
     private void checkPropertyOnDeadlock(SpecificationState ss) {
-        if (studyLiveness) {
-            if (ss.property == false) {
-                livenessInfo.result = false;
-                propertyDone = true;
-            }
-        } else if (studySafety) {
+        if (studySafety) {
             if (safety.safetyType == SafetyProperty.ALLTRACES_ONESTATE && ss.property == false) {
                 safety.result = false;
                 propertyDone = true;
@@ -1775,7 +1723,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             return true;
         }
 
-        if (studyLiveness && propertyDone) {
+        if (studySafety && propertyDone) {
             stoppedConditionReached = true;
             return true;
         }
@@ -1794,7 +1742,7 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
             stoppedConditionReached = false;
         }
 
-        if (studyLiveness) {
+        if (studySafety) {
             stoppedConditionReached = false;
         }
 
@@ -1922,8 +1870,8 @@ public class AvatarModelChecker implements Runnable, myutil.Graph {
         }
 
         int cpt = 0;
-        for (SpecificationLiveness sl : livenesses) {
-            ret.append((cpt + 1) + ". " + sl.toString() + "\n");
+        for (SafetyProperty sp : livenesses) {
+            ret.append((cpt + 1) + ". " + sp.toLivenessString() + "\n");
             cpt++;
         }
         return ret.toString();
diff --git a/src/main/java/avatartranslator/modelchecker/SafetyProperty.java b/src/main/java/avatartranslator/modelchecker/SafetyProperty.java
index 61b0ba8d43..2738b2b46d 100644
--- a/src/main/java/avatartranslator/modelchecker/SafetyProperty.java
+++ b/src/main/java/avatartranslator/modelchecker/SafetyProperty.java
@@ -74,7 +74,7 @@ public class SafetyProperty  {
     public int safetyType;
     public int propertyType;
     public AvatarBlock block;
-    public AvatarStateElement state;
+    public AvatarStateMachineElement state;
     public boolean result;
     
     
@@ -82,6 +82,15 @@ public class SafetyProperty  {
         rawProperty = property.trim();
         analyzeProperty(_spec);
     }
+    
+    public SafetyProperty(AvatarBlock block, AvatarStateMachineElement state) {
+        //create liveness safety
+        this.block = block;
+        this.state = state;
+        propertyType = BLOCK_STATE;
+        safetyType = ALLTRACES_ONESTATE;
+        result = true;
+    }
 
     public boolean analyzeProperty(AvatarSpecification _spec) {
     	String tmpP = rawProperty;
@@ -156,7 +165,7 @@ public class SafetyProperty  {
         return rawProperty;
     }
     
-    public boolean getResult(SpecificationState _ss) {
+    public boolean getSolverResult(SpecificationState _ss) {
         return safetySolver.getResult(_ss) != 0;
     }
 
@@ -175,5 +184,14 @@ public class SafetyProperty  {
             return rawProperty + " -> property is NOT satisfied";
         }
     }
+    
+    public String toLivenessString() {
+        String name = "Element " + state.getExtendedName() + " of block " + block.getName();
+        if (result) {
+            return name + " -> liveness is satisfied"; 
+        } else {
+            return name + " -> liveness is NOT satisfied";
+        }
+    }
 
 }
diff --git a/src/main/java/avatartranslator/modelchecker/SpecificationState.java b/src/main/java/avatartranslator/modelchecker/SpecificationState.java
index f052962154..2daca53757 100644
--- a/src/main/java/avatartranslator/modelchecker/SpecificationState.java
+++ b/src/main/java/avatartranslator/modelchecker/SpecificationState.java
@@ -199,8 +199,8 @@ public class SpecificationState implements Comparable<SpecificationState>  {
     }
 
     public void freeUselessAllocations() {
-	blocks = null;
-	transitions = null;
+        blocks = null;
+        transitions = null;
     }
 
     public int compareTo( SpecificationState _s ) {
diff --git a/src/main/java/ui/window/JDialogAvatarModelChecker.java b/src/main/java/ui/window/JDialogAvatarModelChecker.java
index a6306235d2..2b8882a278 100644
--- a/src/main/java/ui/window/JDialogAvatarModelChecker.java
+++ b/src/main/java/ui/window/JDialogAvatarModelChecker.java
@@ -623,8 +623,8 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
                 res = amc.setLivenessOfSelected();
                 jta.append("Liveness of " + res + " states activated\n");
                 
-                for (SpecificationLiveness sl : amc.getLivenesses()) {
-                    handleLiveness(sl.ref1, false);
+                for (SafetyProperty sp : amc.getLivenesses()) {
+                    handleLiveness(sp.state, false);
                 }
             }
             
@@ -633,8 +633,8 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
                 res = amc.setLivenessOfAllStates();
                 jta.append("Liveness of " + res + " selected elements activated\n");
                 
-                for (SpecificationLiveness sl : amc.getLivenesses()) {
-                    handleLiveness(sl.ref1, false);
+                for (SafetyProperty sp : amc.getLivenesses()) {
+                    handleLiveness(sp.state, false);
                 }
             }
             
@@ -720,8 +720,8 @@ public class JDialogAvatarModelChecker extends javax.swing.JFrame implements Act
                 jta.append("\nLiveness Analysis:\n");
                 jta.append(amc.livenessToString());
                 
-                for (SpecificationLiveness sl : amc.getLivenesses()) {
-                    handleLiveness(sl.ref1, sl.result);
+                for (SafetyProperty sp : amc.getLivenesses()) {
+                    handleLiveness(sp.state, sp.result);
                 }
             }
             
-- 
GitLab