From 306bc9ee452aab7cc6e6910e4a315286cd3b34c2 Mon Sep 17 00:00:00 2001 From: koen <koenwermer@gmail.com> Date: Sun, 8 Jan 2017 00:31:47 +0100 Subject: [PATCH] WLP is now calculated for every method seperately, instead of just for the main method. The code for filling the type environment is now seperated from the rest of the program. --- HelperFunctions.hs | 21 ++++-- Main.hs | 27 ++++--- Settings.hs | 2 +- Tests/2d-arrays1.java | 14 ++-- Tests/arrays1.java | 16 ++-- Tests/methods.java | 10 +-- Types.hs | 79 ++++++++++++++++++++ Verifier.hs | 166 ++++++++++++++++++++++-------------------- WLP.hs | 33 ++++----- 9 files changed, 234 insertions(+), 134 deletions(-) create mode 100644 Types.hs diff --git a/HelperFunctions.hs b/HelperFunctions.hs index fea1187..05c39c0 100644 --- a/HelperFunctions.hs +++ b/HelperFunctions.hs @@ -102,17 +102,17 @@ getMethod' classTypeDecls methodId = case (concatMap searchClass classTypeDecls) getMainMethod :: [TypeDecl] -> Stmt getMainMethod classTypeDecls = fromJust' "getMainMethod" $ getMethod classTypeDecls (Ident "main") +-- Gets a list of all method Idents (except constructor methods) +getMethodIds :: [TypeDecl] -> [Ident] +getMethodIds classTypeDecls = concatMap searchClass classTypeDecls where + searchClass (ClassTypeDecl (ClassDecl _ _ _ _ _ (ClassBody decls))) = searchDecls decls + searchDecls (MemberDecl (MethodDecl _ _ _ id _ _ _):decls) = id : searchDecls decls + searchDecls (_:decls) = searchDecls decls + searchDecls [] = [] + -- Gets the class declarations getDecls :: CompilationUnit -> [TypeDecl] getDecls (CompilationUnit _ _ classTypeDecls) = classTypeDecls - --- Gets the static member declarations and puts them in the type environment -getStaticVars :: CompilationUnit -> TypeEnv -getStaticVars compUnit = concatMap fromTypeDecls (getDecls compUnit) where - fromTypeDecls (ClassTypeDecl (ClassDecl _ _ _ _ _ (ClassBody decls))) = concatMap fromMemberDecl decls - fromMemberDecl (MemberDecl (FieldDecl mods t varDecls)) = if Static `elem` mods then map (fromVarDecl t) varDecls else [] - fromMemberDecl _ = [] - fromVarDecl t (VarDecl varId _) = (Name [getId varId], t) -- Checks if the var is introduced. Introduced variable names start with '$' voor return variables of methods and '#' for other variables isIntroducedVar :: Name -> Bool @@ -130,6 +130,11 @@ invocationToId (MethodCall name _) = getMethodId name invocationToId (PrimaryMethodCall _ _ id _) = id invocationToId _ = undefined +-- Gets the type of the elements of an array. Recursion is needed in the case of multiple dimensional arrays +arrayContentType :: Type -> Type +arrayContentType (RefType (ArrayType t)) = arrayContentType t +arrayContentType t = t + -- Gets a new unique variable getVar :: Ident getVar = Ident ('#' : show (getIncrPointer varPointer)) diff --git a/Main.hs b/Main.hs index 2fe1ca8..5f45bdd 100644 --- a/Main.hs +++ b/Main.hs @@ -4,10 +4,12 @@ import Language.Java.Syntax import Language.Java.Parser import Language.Java.Pretty import Data.List +import Control.Monad import System.FilePath(joinPath) import WLP +import Types import Verifier import HelperFunctions import Settings @@ -22,16 +24,23 @@ main = do case result of Left error -> print error Right compUnit -> do - putStrLn "\r\n-----Code-----" - print compUnit - putStrLn "\r\n-----WLP-----" - let pred = wlpWithEnv (getDecls compUnit) (getStaticVars compUnit) (getMainMethod (getDecls compUnit)) postCond' - putStrLn . prettyPrint $ pred - putStrLn "\r\n-----Correctness-----" - if unsafeIsTrue pred then putStrLn "WLP evaluates to true" else (if unsafeIsFalse pred then putStrLn "WLP evaluates to false" else putStrLn "undecidable") + let decls = getDecls compUnit + let methods = getMethodIds decls + let env = getTypeEnv compUnit decls methods + -- putStrLn "\r\n-----Code-----" + -- print compUnit + mapM_ (testMethod compUnit env decls) methods - - +-- | Calculates and prints the wlp for a given method +testMethod :: CompilationUnit -> TypeEnv -> [TypeDecl] -> Ident -> IO () +testMethod compUnit env decls id = do + putStrLn "--------------" + putStrLn (prettyPrint id ++ "\r\n") + let pred = wlpWithEnv decls env (fromJust' "testMethod" $ getMethod decls id) postCond' + putStrLn . prettyPrint $ pred + putStrLn "" + if unsafeIsTrue env pred then putStrLn "WLP evaluates to true" else (if unsafeIsFalse env pred then putStrLn "WLP evaluates to false" else putStrLn "undecidable") + putStrLn "--------------" -- The post-condition (for testing) postCond' :: Exp diff --git a/Settings.hs b/Settings.hs index 0653c92..0e4b16f 100644 --- a/Settings.hs +++ b/Settings.hs @@ -1,7 +1,7 @@ module Settings where testFile, postCond :: String -testFile = "arrays3" +testFile = "arrays1" postCond = "(true)" nrOfUnroll :: Int diff --git a/Tests/2d-arrays1.java b/Tests/2d-arrays1.java index 9841c30..6d1d2ba 100644 --- a/Tests/2d-arrays1.java +++ b/Tests/2d-arrays1.java @@ -59,9 +59,9 @@ public class Life { } public static boolean occupiedNext(int numNeighbors, boolean occupied){ - if( occupied && (numNeighbors == 2 || numNeighbors == 3)) + if(occupied && ((numNeighbors == 2) || (numNeighbors == 3))) return true; - else if (!occupied && numNeighbors == 3) + else if ((!occupied) && (numNeighbors == 3)) return true; else return false; @@ -69,17 +69,17 @@ public class Life { private static int numNeighbors(boolean[][] world, int row, int col) { int num2 = world[row][col] ? -1 : 0; - for(int r1 = row - 1; r1 <= row + 1; r1++) - for(int c1 = col - 1; c1 <= col + 1; c1++) - if( inbounds(world, r1, c1) && world[r1][c1] ) + for(int r1 = (row - 1); r1 <= (row + 1); r1++) + for(int c1 = (col - 1); c1 <= (col + 1); c1++) + if((inbounds(world, r1, c1)) && (world[r1][c1]) ) num2++; return num2; } private static boolean inbounds(boolean[][] world, int i, int c2) { - return i >= 0 && i < world.length && c2 >= 0 && - c2 < world[0].length; + return (i >= 0) && (i < world.length) && (c2 >= 0) && + (c2 < world[0].length); } diff --git a/Tests/arrays1.java b/Tests/arrays1.java index 9f0ef34..fdffebf 100644 --- a/Tests/arrays1.java +++ b/Tests/arrays1.java @@ -9,7 +9,7 @@ public class ArrayExamples findAndPrintPairs(list, 5); bubblesort(list); - /* + showList(list); list = new int[11]; @@ -23,14 +23,14 @@ public class ArrayExamples list = new int[1]; bubblesort(list); showList(list); - */ + } // pre: list != null, list.length > 0 // post: return index of minimum element of array public static int findMin(int[] list2) - { assert list2 != null && list2.length > 0 : "failed precondition"; + { assert (list2 != null) && (list2.length > 0) : "failed precondition"; int indexOfMin = 0; for(int k = 1; k < list2.length; k++) @@ -49,7 +49,7 @@ public class ArrayExamples * argument */ public static void badResize(int[] list3, int newSize) - { assert list3 != null && newSize >= 0 : "failed precondition"; + { assert (list3 != null) && (newSize >= 0) : "failed precondition"; int[] temp = new int[newSize]; int limit = Math.min(list3.length, newSize); @@ -70,7 +70,7 @@ public class ArrayExamples * will be copied into the new array */ public static int[] goodResize(int[] list4, int newSize) - { assert list4 != null && newSize >= 0 : "failed precondition"; + { assert (list4 != null) && (newSize >= 0) : "failed precondition"; int[] result = new int[newSize]; int limit = Math.min(list4.length, newSize); @@ -110,7 +110,7 @@ public class ArrayExamples { assert list6 != null : "failed precondition"; - int temp; + int temp2; boolean changed = true; for(int n = 0; (n < list6.length) && changed; n++) { changed = false; @@ -119,9 +119,9 @@ public class ArrayExamples "is out of bounds."; if(list6[o] > list6[o+1]) { changed = true; - temp = list6[o + 1]; + temp2 = list6[o + 1]; list6[o + 1] = list6[o]; - list6[o] = temp; + list6[o] = temp2; } } } diff --git a/Tests/methods.java b/Tests/methods.java index 47b8dfd..661059a 100644 --- a/Tests/methods.java +++ b/Tests/methods.java @@ -6,8 +6,8 @@ public static class Main C c1, c2; c1 = new C(1); c2 = new C(0); - c1.method1(1); - c2.method1(1); + c1.method1(1, true); + c2.method1(1, true); x = c1.c + c2.c; // C.staticMethod(); } @@ -34,12 +34,12 @@ public class C x = 4; } - public void method1(int n) + public void method1(int n, boolean b) { - + assert b; this.c += n; if(this.c < 2) - this.method1(); + this.method1(); } public int method2() diff --git a/Types.hs b/Types.hs new file mode 100644 index 0000000..26d984d --- /dev/null +++ b/Types.hs @@ -0,0 +1,79 @@ +module Types where + +import Language.Java.Syntax +import Data.Maybe +import Data.List + +import Folds +import HelperFunctions +import Settings + + +typesStmtAlgebra :: StmtAlgebra TypeEnv +typesStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhancedFor, fEmpty, fExpStmt, fAssert, fSwitch, fDo, fBreak, fContinue, fReturn, fSynchronized, fThrow, fTry, fLabeled) where + fStmtBlock b = envBlock b + fIfThen e s1 = s1 + fIfThenElse e s1 s2 = s1 ++ s2 + fWhile e s = s + fBasicFor init me incr s = case init of + Just (ForLocalVars mods t vars) -> foldr (\v env' -> (varName v, t):env') s vars + _ -> s + fEnhancedFor = error "EnhancedFor" + fEmpty = [] + fExpStmt e = [] + fAssert e _ = [] + fSwitch e bs = concatMap envSwitch bs + fDo s e = s + fBreak _ = [] + fContinue _ = [] + fReturn me = [] + fSynchronized _ _ = [] + fThrow e = [] + fTry b cs f = envBlock b ++ concatMap envCatch cs ++ + (case f of + Just b -> envBlock b + _ -> []) + fLabeled _ s = s + + + -- Adds declarations within a block to a type environment + envBlock :: Block -> TypeEnv + envBlock (Block bs) = foldl f [] bs + where f env (LocalVars mods t vars) = foldr (\v env' -> (varName v, t):env') env vars + f env (BlockStmt s) = foldStmt typesStmtAlgebra s ++ env + f env _ = env + + varName (VarDecl (VarId id) _) = Name [id] + + -- Adds declarations within a switchblock block to a type environment + envSwitch :: SwitchBlock -> TypeEnv + envSwitch (SwitchBlock _ bs) = envBlock (Block bs) + + -- Adds declarations within a catchblock block to a type environment + envCatch :: Catch -> TypeEnv + envCatch (Catch (FormalParam _ t _ varId) b) = (Name [getId varId], t) : envBlock b + + +-- Gets the types of all variables that are declared in a statement +getStmtTypeEnv :: Stmt -> TypeEnv +getStmtTypeEnv = foldStmt typesStmtAlgebra + +-- Gets the parameters of a function and puts them in the type environment +getMethodTypeEnv :: [TypeDecl] -> Ident -> TypeEnv +getMethodTypeEnv decls id = case getMethodParams decls id of + Nothing -> [] + Just params -> map fromParam params + where + fromParam (FormalParam _ t _ paramId) = (Name [getId paramId], t) + +-- Gets the static member declarations and puts them in the type environment +getStaticVarsTypeEnv :: CompilationUnit -> TypeEnv +getStaticVarsTypeEnv compUnit = concatMap fromTypeDecls (getDecls compUnit) where + fromTypeDecls (ClassTypeDecl (ClassDecl _ _ _ _ _ (ClassBody decls))) = concatMap fromMemberDecl decls + fromMemberDecl (MemberDecl (FieldDecl mods t varDecls)) = if Static `elem` mods then map (fromVarDecl t) varDecls else [] + fromMemberDecl _ = [] + fromVarDecl t (VarDecl varId _) = (Name [getId varId], t) + +-- Gets the type env for a compilation unit +getTypeEnv :: CompilationUnit -> [TypeDecl] -> [Ident] -> TypeEnv +getTypeEnv compUnit decls methods = getStaticVarsTypeEnv compUnit ++ concatMap (getMethodTypeEnv decls) methods ++ concatMap (getStmtTypeEnv . fromJust . getMethod decls) methods \ No newline at end of file diff --git a/Verifier.hs b/Verifier.hs index d86c402..c2406e9 100644 --- a/Verifier.hs +++ b/Verifier.hs @@ -10,15 +10,15 @@ import HelperFunctions -- | Checks wether the negation is unsatisfiable -isTrue :: Exp -> Z3 Bool -isTrue e = isFalse (PreNot e) +isTrue :: TypeEnv -> Exp -> Z3 Bool +isTrue env e = isFalse env (PreNot e) -- | Checks wether the expression is unsatisfiable -isFalse :: Exp -> Z3 Bool -isFalse e = +isFalse :: TypeEnv -> Exp -> Z3 Bool +isFalse env e = do - ast <- foldExp expAssertAlgebra e + ast <- foldExp expAssertAlgebra e env assert ast result <- check solverReset @@ -27,12 +27,12 @@ isFalse e = _ -> return False -- | Unsafe version of isTrue -unsafeIsTrue :: Exp -> Bool -unsafeIsTrue = unsafePerformIO . evalZ3 . isTrue +unsafeIsTrue :: TypeEnv -> Exp -> Bool +unsafeIsTrue env = unsafePerformIO . evalZ3 . isTrue env -- | Unsafe version of isFalse -unsafeIsFalse :: Exp -> Bool -unsafeIsFalse = unsafePerformIO . evalZ3 . isFalse +unsafeIsFalse :: TypeEnv -> Exp -> Bool +unsafeIsFalse env = unsafePerformIO . evalZ3 . isFalse env stringToBv :: String -> Z3 AST stringToBv [] = mkIntNum 0 >>= mkInt2bv 8 @@ -44,9 +44,9 @@ stringToBv (c:cs) = do -- | Defines the convertion from an expression to AST so that Z3 can assert satisfiability -- This is used to fold expressions generated by the WLP transformer, so not all valid Java expressions need to be handled -expAssertAlgebra :: ExpAlgebra (Z3 AST) +expAssertAlgebra :: ExpAlgebra (TypeEnv -> Z3 AST) expAssertAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualInstanceCreation, fArrayCreate, fArrayCreateInit, fFieldAccess, fMethodInv, fArrayAccess, fExpName, fPostIncrement, fPostDecrement, fPreIncrement, fPreDecrement, fPrePlus, fPreMinus, fPreBitCompl, fPreNot, fCast, fBinOp, fInstanceOf, fCond, fAssign, fLambda, fMethodRef) where - fLit lit = case lit of + fLit lit _ = case lit of Int n -> mkInteger n Word n -> mkInteger n Float d -> mkRealNum d @@ -62,127 +62,139 @@ expAssertAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQual fThisClass = undefined fInstanceCreation = undefined fQualInstanceCreation = undefined - fArrayCreate = undefined + fArrayCreate = error "ArrayCreate" fArrayCreateInit = undefined - fFieldAccess fieldAccess = case fieldAccess of + fFieldAccess fieldAccess _ = case fieldAccess of PrimaryFieldAccess e id -> case e of InstanceCreation _ t args _ -> undefined _ -> undefined SuperFieldAccess id -> mkStringSymbol (prettyPrint (Name [id])) >>= mkIntVar ClassFieldAccess (Name name) id -> mkStringSymbol (prettyPrint (Name (name ++ [id]))) >>= mkIntVar - fMethodInv invocation = case invocation of + fMethodInv invocation env = case invocation of MethodCall (Name [Ident "*length"]) [a, (Lit (Int n))] -> case a of - ArrayCreate t exps dim -> foldExp expAssertAlgebra (if fromEnum n < length exps then (exps !! fromEnum n) else Lit (Int 0)) + ArrayCreate t exps dim -> foldExp expAssertAlgebra (if fromEnum n < length exps then (exps !! fromEnum n) else Lit (Int 0)) env ArrayCreateInit t dim arrayInit -> mkInteger 0 - _ -> error "length of non-array" + ExpName name -> do + symbol <- mkStringSymbol ("*length(" ++ prettyPrint name ++ ", " ++ show n ++ ")") + mkIntVar symbol + Cond g a1 a2 -> foldExp expAssertAlgebra (Cond g (MethodInv (MethodCall (Name [Ident "*length"]) [a1, (Lit (Int n))])) (MethodInv (MethodCall (Name [Ident "*length"]) [a2, (Lit (Int n))]))) env + _ -> error ("length of non-array: " ++ prettyPrint a) _ -> error (prettyPrint invocation) - fArrayAccess arrayIndex = case arrayIndex of - ArrayIndex (ArrayCreate t _ _) _ -> foldExp expAssertAlgebra (getInitValue t) - ArrayIndex (ArrayCreateInit t _ _) _ -> foldExp expAssertAlgebra (getInitValue t) - ArrayIndex e _ -> foldExp expAssertAlgebra e - fExpName name = do - symbol <- mkStringSymbol (prettyPrint name) - mkIntVar symbol + fArrayAccess arrayIndex env = case arrayIndex of + ArrayIndex (ArrayCreate t _ _) _ -> foldExp expAssertAlgebra (getInitValue t) env + ArrayIndex (ArrayCreateInit t _ _) _ -> foldExp expAssertAlgebra (getInitValue t) env + ArrayIndex (ExpName name) i -> do + symbol <- mkStringSymbol (prettyPrint name ++ "[" ++ show i ++ "]") + case fmap arrayContentType (lookup name env) of + Just (PrimType BooleanT) -> mkBoolVar symbol + Just (PrimType FloatT) -> mkRealVar symbol + Just (PrimType DoubleT) -> mkRealVar symbol + _ -> mkIntVar symbol + ArrayIndex (Cond g a1 a2) i -> foldExp expAssertAlgebra (Cond g (ArrayAccess (ArrayIndex a1 i)) (ArrayAccess (ArrayIndex a2 i))) env + ArrayIndex e _ -> foldExp expAssertAlgebra e env + fExpName name env = do + symbol <- mkStringSymbol (prettyPrint name) + case lookup name env of + Just (PrimType BooleanT) -> mkBoolVar symbol + Just (PrimType FloatT) -> mkRealVar symbol + Just (PrimType DoubleT) -> mkRealVar symbol + _ -> mkIntVar symbol fPostIncrement = undefined fPostDecrement = undefined fPreIncrement = undefined fPreDecrement = undefined - fPrePlus e = e - fPreMinus e = do - ast <- e - zero <- mkInteger 0 - mkSub [zero, ast] + fPrePlus e env = e env + fPreMinus e env = do + ast <- e env + zero <- mkInteger 0 + mkSub [zero, ast] fPreBitCompl = undefined - fPreNot e = e >>= mkNot + fPreNot e env = e env >>= mkNot fCast = undefined - fBinOp e1 op e2 = case op of + fBinOp e1 op e2 env = case op of Mult -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkMul [ast1, ast2] Div -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkDiv ast1 ast2 Rem -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkRem ast1 ast2 Add -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkAdd [ast1, ast2] Sub -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkSub [ast1, ast2] LShift -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkBvshl ast1 ast2 RShift -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkBvashr ast1 ast2 RRShift -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkBvlshr ast1 ast2 LThan -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkLt ast1 ast2 GThan -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkGt ast1 ast2 LThanE -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkLe ast1 ast2 GThanE -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkGe ast1 ast2 Equal -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkEq ast1 ast2 NotEq -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env eq <- mkEq ast1 ast2 mkNot eq And-> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkAnd [ast1, ast2] Or -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkOr [ast1, ast2] Xor -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkXor ast1 ast2 CAnd -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkAnd [ast1, ast2] COr -> do - ast1 <- e1 - ast2 <- e2 + ast1 <- e1 env + ast2 <- e2 env mkOr [ast1, ast2] fInstanceOf = undefined - fCond g e1 e2 = do - astg <- (g >>= mkNot) - assert astg - result <- check - solverReset - case result of - Sat -> e2 - Unsat -> e1 - _ -> error "can't evaluate if-condition" + fCond g e1 e2 env = do + astg <- g env + ast1 <- e1 env + ast2 <- e2 env + mkIte astg ast1 ast2 fAssign = undefined fLambda = undefined fMethodRef = undefined \ No newline at end of file diff --git a/WLP.hs b/WLP.hs index 8ebf692..c84dd05 100644 --- a/WLP.hs +++ b/WLP.hs @@ -34,7 +34,7 @@ type Syn = Exp -> Exp -- The wlp transformer -- Statements that pass control to the next statement have to explicitly combine their wlp function with the accumulated function, as some statements (e.g. break) ignore the accumulated function. wlpStmtAlgebra :: StmtAlgebra (Inh -> Syn) wlpStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhancedFor, fEmpty, fExpStmt, fAssert, fSwitch, fDo, fBreak, fContinue, fReturn, fSynchronized, fThrow, fTry, fLabeled) where - fStmtBlock (Block bs) inh = foldr (\b r -> wlpBlock (inh {acc = r, env = envBlock bs (env inh)}) b) (acc inh) bs -- The result of the last block-statement will be the accumulated transformer for the second-last etc. The type environment is build from the left, so it has to be done seperately. + fStmtBlock (Block bs) inh = foldr (\b r -> wlpBlock inh {acc = r} b) (acc inh) bs -- The result of the last block-statement will be the accumulated transformer for the second-last etc. The type environment is build from the left, so it has to be done seperately. fIfThen e s1 = fIfThenElse e s1 (const id) -- if-then is just an if-then-else with an empty else-block fIfThenElse e s1 s2 inh = let (e', trans) = foldExp wlpExpAlgebra e inh {acc = id} var = getVar @@ -60,7 +60,7 @@ wlpStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhanced fThrow e inh = case catch inh of Nothing -> ((\q -> q &* throwException e)) -- acc is ignored, as the rest of the block is not executed Just (cs, f) -> (maybe (if f then id else (\q -> q &* throwException e)) (flip fStmtBlock (inh {acc = id, catch = Nothing})) (getCatch (decls inh) (env inh) e cs)) - fTry (Block bs) cs f inh = let r = (fStmtBlock (Block bs) (inh {acc = id, catch = Just (cs, isJust f)})) in (r . maybe (acc inh) (flip fStmtBlock (inh {env = envBlock bs (env inh)})) f) -- The finally-block is always executed + fTry (Block bs) cs f inh = let r = (fStmtBlock (Block bs) (inh {acc = id, catch = Just (cs, isJust f)})) in (r . maybe (acc inh) (flip fStmtBlock inh) f) -- The finally-block is always executed fLabeled _ s = s -- Helper functions @@ -71,14 +71,6 @@ wlpStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhanced BlockStmt s -> wlp' inh s LocalClass _ -> (acc inh) LocalVars mods t vars -> foldr (\v r -> (wlpDeclAssignment t (inh {acc = r}) v)) (acc inh) vars - - -- Adds declarations within a block to a type environment - envBlock :: [BlockStmt] -> TypeEnv -> TypeEnv - envBlock bs env = foldl f env bs - where f env (LocalVars mods t vars) = foldr (\v env' -> (varName v, t):env') env vars - f env (BlockStmt (BasicFor (Just (ForLocalVars mods t vars)) _ _ s)) = foldr (\v env' -> (varName v, t):env') env vars - f env _ = env - varName (VarDecl (VarId id) _) = Name [id] -- wlp of a var declaration that also assigns a value. Declaring without assignment assigns the default value wlpDeclAssignment :: Type -> Inh -> VarDecl -> Exp -> Exp @@ -149,7 +141,7 @@ wlpExpAlgebra :: ExpAlgebra (Inh -> (Exp, Syn)) wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualInstanceCreation, fArrayCreate, fArrayCreateInit, fFieldAccess, fMethodInv, fArrayAccess, fExpName, fPostIncrement, fPostDecrement, fPreIncrement, fPreDecrement, fPrePlus, fPreMinus, fPreBitCompl, fPreNot, fCast, fBinOp, fInstanceOf, fCond, fAssign, fLambda, fMethodRef) where fLit lit inh = (Lit lit, (acc inh)) fClassLit mType inh = (ClassLit mType, (acc inh)) - fThis inh = (fromJust' "fThis" (object inh), (acc inh)) + fThis inh = (fromJust' "fThis" (object inh), acc inh) fThisClass name inh = (ThisClass name, (acc inh)) fInstanceCreation typeArgs t args mBody inh = case args of [ExpName (Name [Ident "#"])] -> (InstanceCreation typeArgs t args mBody, acc inh) -- '#' indicates we already called the constructor method using the correct arguments @@ -161,7 +153,7 @@ wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualIns fQualInstanceCreation e typeArgs t args mBody inh = error "fQualInstanceCreation" fArrayCreate t dimLengths dim inh = (ArrayCreate t (map (\e -> fst (e inh)) dimLengths) dim, acc inh) fArrayCreateInit t dim init inh = error "ArrayCreateInit" -- (ArrayCreateInit t dim init, acc inh) - fFieldAccess fieldAccess inh = (ExpName (foldFieldAccess inh fieldAccess), (acc inh)) + fFieldAccess fieldAccess inh = (foldFieldAccess inh fieldAccess, (acc inh)) fMethodInv invocation inh = case invocation of MethodCall (Name [Ident "*assume"]) [e] -> (false, (if e == false then const true else imp e)) -- *assume is the regular assume function _ -> if getCallCount (calls inh) (invocationToId invocation) >= nrOfUnroll -- Check the recursion depth @@ -276,20 +268,23 @@ wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualIns foldLhs inh lhs = case lhs of FieldLhs (PrimaryFieldAccess e id) -> case foldExp wlpExpAlgebra e inh of (ExpName name, trans) -> (NameLhs (Name (fromName name ++ [id])), trans) - _ -> error "foldFieldAccess" + _ -> error "foldLhs" ArrayLhs (ArrayIndex a i) -> let (a', aTrans) = foldExp wlpExpAlgebra a inh i' = map (\x -> foldExp wlpExpAlgebra x inh) i in (ArrayLhs (ArrayIndex a' (map fst i')), foldl (\trans (_, iTrans) -> trans . iTrans) aTrans i' . arrayAccessWlp a' (map fst i') inh) lhs' -> (lhs', id) - -- Folds the expression part of a fieldaccess and simplifies it to a name - foldFieldAccess :: Inh -> FieldAccess -> Name + -- Folds the expression part of a fieldaccess and simplifies it + foldFieldAccess :: Inh -> FieldAccess -> Exp foldFieldAccess inh fieldAccess = case fieldAccess of PrimaryFieldAccess e id -> case fst (foldExp wlpExpAlgebra e inh) of - ExpName name -> Name (fromName name ++ [id]) - x -> error ("foldFieldAccess: " ++ show x ++ show id) + ExpName name -> ExpName (Name (fromName name ++ [id])) + ArrayAccess (ArrayIndex a i) -> let (a', aTrans) = foldExp wlpExpAlgebra a inh + i' = map (\x -> foldExp wlpExpAlgebra x inh) i + in MethodInv (MethodCall (Name [Ident "*length"]) [a, (Lit (Int (toEnum (length i'))))]) + x -> error ("foldFieldAccess: " ++ show x ++ " . " ++ show id) SuperFieldAccess id -> foldFieldAccess inh (PrimaryFieldAccess (fromJust' "foldFieldAccess" (object inh)) id) - ClassFieldAccess name id -> Name (fromName name ++ [id]) + ClassFieldAccess name id -> ExpName (Name (fromName name ++ [id])) -- Simplified version of substVar, handy to use with introduced variables @@ -302,7 +297,7 @@ wlp decls = wlpWithEnv decls [] -- | wlp with a given type environment wlpWithEnv :: [TypeDecl] -> TypeEnv -> Stmt -> Exp -> Exp -wlpWithEnv decls env = wlp' (Inh id id Nothing env decls [] Nothing Nothing) +wlpWithEnv decls env = wlp' (Inh id id Nothing env decls [] Nothing (Just (ExpName (Name [Ident "#obj"])))) -- wlp' lets you specify the inherited attributes wlp' :: Inh -> Stmt -> Syn -- GitLab