From f0a5a6f8e48fb5b10ceacb5d7b16cf8f2eaf939d Mon Sep 17 00:00:00 2001 From: koen <koenwermer@gmail.com> Date: Tue, 6 Dec 2016 20:18:36 +0100 Subject: [PATCH] started with method calls. Method calls on objects are still very buggy --- HelperFunctions.hs | 62 +++++++++++++++++++++++++++++++++++++++- Main.hs | 23 ++------------- Settings.hs | 2 +- Tests/methods.java | 30 ++++++++++++++++++++ WLP.hs | 71 +++++++++++++++++++++++++++++++++++++--------- 5 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 Tests/methods.java diff --git a/HelperFunctions.hs b/HelperFunctions.hs index 1db0c74..6512372 100644 --- a/HelperFunctions.hs +++ b/HelperFunctions.hs @@ -5,7 +5,8 @@ import Language.Java.Syntax import Language.Java.Pretty import Data.Maybe -type TypeEnv = [(Name, Type)] +type TypeEnv = [(Name, Type)] +type CallCount = [(Ident, Int)] -- | Retrieves the type from the environment lookupType :: [TypeDecl] -> TypeEnv -> Name -> Type @@ -33,9 +34,62 @@ getFieldType decls (RefType (ClassRefType t)) (Name (f:fs)) = getFieldType decls _ -> getDecl t xs getDecl _ _ = error "nested class" +-- Increments the call count for a given method +incrCallCount :: CallCount -> Ident -> CallCount +incrCallCount [] id = [(id, 1)] +incrCallCount ((id', c):xs) id = if id == id' then (id', c + 1) : xs else (id', c) : incrCallCount xs id + + +-- Looks up the call count for a given method +getCallCount :: CallCount -> Ident -> Int +getCallCount [] id = 0 +getCallCount ((id', c):xs) id = if id == id' then c else getCallCount xs id + getId :: VarDeclId -> Ident getId (VarId id) = id getId (VarDeclArray id) = getId id + +fromName :: Name -> [Ident] +fromName (Name name) = name + +-- Gets the ident of the method from a name +getMethodId :: Name -> Ident +getMethodId = last . fromName + +-- Gets the statement(-block) defining a method +getMethod :: [TypeDecl] -> Ident -> Stmt +getMethod classTypeDecls methodId = fst (getMethod' classTypeDecls methodId) + +-- Gets the return type of a method +getMethodType :: [TypeDecl] -> Ident -> Maybe Type +getMethodType classTypeDecls methodId = snd (getMethod' classTypeDecls methodId) + +-- Finds a method definition. This function assumes all methods are named differently +getMethod' :: [TypeDecl] -> Ident -> (Stmt, Maybe Type) +getMethod' classTypeDecls methodId = case (concatMap searchClass classTypeDecls) of + r:_ -> r + [] -> error ("non-existing method: " ++ show methodId) + where + searchClass (ClassTypeDecl (ClassDecl _ _ _ _ _ (ClassBody decls))) = searchDecls decls + searchDecls (MemberDecl (MethodDecl _ _ t id _ _ (MethodBody (Just b))):_) | methodId == id = [(StmtBlock b, t)] + searchDecls (_:decls) = searchDecls decls + searchDecls [] = [] + +-- Gets the statement(-block) defining the main method and initializes the heap +getMainMethod :: [TypeDecl] -> Stmt +getMainMethod classTypeDecls = getMethod classTypeDecls (Ident "main") + +-- 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) true :: Exp true = Lit (Boolean True) @@ -70,6 +124,12 @@ arrayAccess a i = case a of ArrayCreateInit t dim arrayInit -> getInitValue t _ -> ArrayAccess (ArrayIndex a i) +-- Accesses fields of fields +fieldAccess :: Exp -> Name -> FieldAccess +fieldAccess e (Name [id]) = PrimaryFieldAccess e id +fieldAccess e (Name (id:ids)) = fieldAccess (FieldAccess (PrimaryFieldAccess e id)) (Name ids) +fieldAccess _ _ = error "FieldAccess without field name" + -- | Gets the initial value for a given type getInitValue :: Type -> Exp getInitValue (PrimType t) = case t of diff --git a/Main.hs b/Main.hs index 970cd4a..2fe1ca8 100644 --- a/Main.hs +++ b/Main.hs @@ -22,33 +22,16 @@ 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) (getStmt compUnit) postCond' + 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") --- Gets the statement(-block) defining the main method and initializes the heap -getStmt :: CompilationUnit -> Stmt -getStmt (CompilationUnit _ _ classTypeDecls) = (head (concatMap getInit classTypeDecls)) where - getInit (ClassTypeDecl (ClassDecl _ _ _ _ _ (ClassBody decls))) = getInit' decls - getInit' [] = [] - getInit' (MemberDecl (MethodDecl _ _ _ (Ident "main") _ _ (MethodBody (Just b))):_) = [StmtBlock b] - getInit' (_:decls) = getInit' decls - --- 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) -- The post-condition (for testing) postCond' :: Exp diff --git a/Settings.hs b/Settings.hs index c8ba2e8..689be15 100644 --- a/Settings.hs +++ b/Settings.hs @@ -1,7 +1,7 @@ module Settings where testFile, postCond :: String -testFile = "loops" +testFile = "methods" postCond = "x == 5" nrOfUnroll :: Int diff --git a/Tests/methods.java b/Tests/methods.java new file mode 100644 index 0000000..1fd298d --- /dev/null +++ b/Tests/methods.java @@ -0,0 +1,30 @@ +public static class Main +{ + static int x; + public static void main(String[] args) + { + C1 c1 = new C1(); + c1.c += 2; //c1.method1(); + x = c1.c; + } + + + + public static void method() + { + } +} + +public class C1 +{ + int c; + + public C1() + { + } + + public void method1() + { + // c++; + } +} \ No newline at end of file diff --git a/WLP.hs b/WLP.hs index ab0cc72..675a171 100644 --- a/WLP.hs +++ b/WLP.hs @@ -15,11 +15,14 @@ import Settings -- | A type for the inherited attribute -data Inh = Inh {acc :: Exp -> Exp, -- The accumulated transformer of the current block up until the current statement - br :: Exp -> Exp, -- The accumulated transformer up until the last loop (this is used when handling break statements etc.) - catch :: Maybe ([Catch], Bool), -- The catches when executing a block in a try statement, and a Bool indicating wether there is a finally-block - env :: TypeEnv, -- The type environment for typing expressions - decls :: [TypeDecl] -- Class declarations +data Inh = Inh {acc :: Exp -> Exp, -- The accumulated transformer of the current block up until the current statement + br :: Exp -> Exp, -- The accumulated transformer up until the last loop (this is used when handling break statements etc.) + catch :: Maybe ([Catch], Bool), -- The catches when executing a block in a try statement, and a Bool indicating wether there is a finally-block + env :: TypeEnv, -- The type environment for typing expressions + decls :: [TypeDecl], -- Class declarations + calls :: CallCount, -- The number of recursive calls per method + ret :: Maybe Ident, -- The name of the return variable when handling a method call + object :: Maybe Exp -- The object the method is called from when handling a method call } @@ -49,7 +52,7 @@ wlpStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhanced fContinue _ inh = (id, env inh) -- at a continue statement it's as if the body of the loop is fully executed fReturn me inh = case me of Nothing -> (id, env inh) -- Return ignores acc, as it terminates the method - Just e -> fExpStmt (Assign (NameLhs (Name [Ident "return"])) EqualA e) inh -- We treat "return e" as an assignment to the variable return + Just e -> fExpStmt (Assign (NameLhs (Name [fromJust (ret inh)])) EqualA e) (inh {acc = id}) -- We treat "return e" as an assignment to a variable specifically created to store the return value in fSynchronized _ = fStmtBlock fThrow e inh = case catch inh of @@ -81,7 +84,7 @@ wlpStmtAlgebra = (fStmtBlock, fIfThen, fIfThenElse, fWhile, fBasicFor, fEnhanced PrimType _ -> substVar (env inh) (decls inh) (NameLhs (Name [ident])) (getInitValue t) . acc inh -- We don't initialize ref types to null, because we want to keep pointer information RefType _ -> acc inh - wlpDeclAssignment t inh (VarDecl (VarId ident) (Just (InitExp e))) = substVar (env inh) (decls inh) (NameLhs (Name [ident])) e . acc inh + wlpDeclAssignment t inh (VarDecl (VarId ident) (Just (InitExp e))) = getTrans (foldExp wlpExpAlgebra (Assign (NameLhs (Name [ident])) EqualA e)) inh -- Unrolls a while-loop a finite amount of times unrollLoop :: Int -> Exp -> (Exp -> Exp) -> Exp -> Exp @@ -148,12 +151,15 @@ wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualIns fArrayCreate t dimLengths dim inh = (ArrayCreate t (map (flip getExp inh) dimLengths) dim, (acc inh, env inh)) fArrayCreateInit t dim init inh = (ArrayCreateInit t dim init, (acc inh, env inh)) fFieldAccess fieldAccess inh = (FieldAccess fieldAccess, (acc inh, env inh)) - fMethodInv invocation inh = (Lit Null, (acc inh, env inh)) + fMethodInv invocation inh = (ExpName (Name [getReturnVar inh invocation]), + (if getCallCount (calls inh) (invocationToId invocation) >= nrOfUnroll + then const true + else fst (wlp' (inh {env = maybe (env inh) (\t -> (Name [getReturnVar inh invocation], t) : env inh) (getType inh invocation), calls = incrCallCount (calls inh) (invocationToId invocation), ret = Just (getReturnVar inh invocation), object = Just (getObject invocation)}) (inlineMethod inh invocation)) . acc inh, env inh)) fArrayAccess (ArrayIndex a i) inh = case catch inh of Nothing -> (arrayAccess a i, (acc inh, env inh)) Just (cs, f) -> (arrayAccess a i, (arrayAccessWlp a i inh, env inh)) - fExpName name inh = (ExpName name, (acc inh, env inh)) + fExpName name inh = (varInObject (object inh) name, (acc inh, env inh)) -- x++ increments x but evaluates to the original value fPostIncrement e inh = case getExp e inh of var@(ExpName name) -> (var, (substVar (env inh) (decls inh) (NameLhs name) (BinOp var Add (Lit (Int 1))) . acc inh, env inh)) @@ -176,8 +182,9 @@ wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualIns fBinOp e1 op e2 inh = (BinOp (getExp e1 inh) op (getExp e2 inh), (getTrans e1 (inh {acc = getTrans e2 inh}), env inh)) fInstanceOf = error "instanceOf" fCond g e1 e2 inh = (Cond (getExp g inh) (getExp e1 inh) (getExp e2 inh), (getTrans g (inh {acc = id}) . (\q -> (getExp g inh &* getTrans e1 (inh {acc = id}) q) |* (neg (getExp g inh) &* getTrans e2 (inh {acc = id}) q)) . acc inh, env inh)) - fAssign lhs op e inh = let rhs = desugarAssign lhs op (getExp e inh) - in (rhs, (substVar (env inh) (decls inh) lhs rhs . getTrans e inh, env inh)) + fAssign lhs op e inh = let lhs' = lhsInObject (object inh) lhs + rhs' = desugarAssign lhs' op (getExp e inh) + in (rhs', (substVar (env inh) (decls inh) lhs' rhs' . getTrans e inh, env inh)) fLambda = error "lambda" fMethodRef = error "method reference" @@ -191,6 +198,44 @@ wlpExpAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualIns dimLengths a = case a of ArrayCreate t exps dim -> exps _ -> map (\n -> MethodInv (MethodCall (Name [Ident "*length"]) [a, (Lit (Int n))])) [0..] + + -- Inlines a methodcall. This creates a variable to store the return value in + inlineMethod :: Inh -> MethodInvocation -> Stmt + inlineMethod inh (MethodCall name args) = getMethod (decls inh) (getMethodId name) + inlineMethod inh (PrimaryMethodCall _ _ id args) = getMethod (decls inh) id + inlineMethod inh _ = undefined + + -- Gets the variable that represents the return value of the method + getReturnVar :: Inh -> MethodInvocation -> Ident + getReturnVar inh invocation = Ident (show (invocationToId invocation) ++ show (getCallCount (calls inh) (invocationToId invocation))) + + -- Gets the object a method is called from + getObject :: MethodInvocation -> Exp + getObject (MethodCall name _) = ExpName (Name (take (length (fromName name) - 1) (fromName name))) + getObject (PrimaryMethodCall e _ _ _) = e + getObject _ = undefined + + -- Gets the return type of a method + getType :: Inh -> MethodInvocation -> Maybe Type + getType inh invocation = getMethodType (decls inh) (invocationToId invocation) + + -- Gets the method Id from an invocation + invocationToId :: MethodInvocation -> Ident + invocationToId (MethodCall name _) = getMethodId name + invocationToId (PrimaryMethodCall _ _ id _) = id + invocationToId _ = undefined + + -- Changes the lhs to refer to a field of a given object + lhsInObject :: Maybe Exp -> Lhs -> Lhs + lhsInObject Nothing lhs = lhs + lhsInObject (Just obj) lhs = case lhs of + NameLhs name -> FieldLhs (fieldAccess obj name) + _ -> error "TODO: lhsInObject" + + -- Changes the var to refer to a field of a given object + varInObject :: Maybe Exp -> Name -> Exp + varInObject Nothing name = ExpName name + varInObject (Just obj) name = FieldAccess (fieldAccess obj name) -- | Gets the expression attribute getExp :: (Inh -> (Exp, Syn)) -> Inh -> Exp @@ -206,11 +251,11 @@ getEnv f inh = let (_, (_, env)) = f inh in env -- | Calculates the weakest liberal pre-condition of a statement and a given post-condition wlp :: [TypeDecl] -> Stmt -> Exp -> Exp -wlp decls = fst . (wlp' (Inh id id Nothing [] decls)) +wlp decls = fst . (wlp' (Inh id id Nothing [] decls [] Nothing Nothing)) -- | wlp with a given type environment wlpWithEnv :: [TypeDecl] -> TypeEnv -> Stmt -> Exp -> Exp -wlpWithEnv decls env = fst . (wlp' (Inh id id Nothing env decls)) +wlpWithEnv decls env = fst . (wlp' (Inh id id Nothing env decls [] Nothing Nothing)) -- wlp' lets you specify the inherited attributes wlp' :: Inh -> Stmt -> Syn -- GitLab