Skip to content
Snippets Groups Projects
SimpleFormulaChecker.hs 4.29 KiB
Newer Older
  • Learn to ignore specific revisions
  • Orestis Melkonian's avatar
    Orestis Melkonian committed
    module SimpleFormulaChecker where
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    import Control.Exception.Base (catch)
    import Control.Monad (when)
    import Control.Monad.Trans (liftIO)
    import qualified Data.Map as M
    import Data.Maybe
    
    
    import Javawlp.Engine.HelperFunctions
    
    import Javawlp.Engine.Types
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    import Language.Java.Parser
    import Language.Java.Pretty
    import Language.Java.Syntax
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    import qualified LogicIR.Backend.API as Z3
    
    import LogicIR.Eval
    
    import LogicIR.Frontend.Java
    import LogicIR.Backend.Z3
    
    import LogicIR.Backend.Pretty
    
    import LogicIR.Backend.Null
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    import LogicIR.Frontend.Java (javaExpToLExpr)
    import LogicIR.Null (lExprPreprocessNull)
    import LogicIR.Pretty (prettyLExpr)
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    -- | Response type.
    data Response = Equivalent | NotEquivalent (Maybe String) | Undefined | Timeout
                    deriving (Eq, Show)
    (<>) :: Response -> Response -> Response
    Equivalent <> r = r
    NotEquivalent s <> _ = NotEquivalent s
    Timeout <> _ = Timeout
    Undefined <> _ = Undefined
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    -- Function that compares both the pre and the post condition for two methods.
    -- It is assumed that both methods have the same environment (parameter names, class member names, etc).
    compareSpec :: (FilePath, String) -> (FilePath, String) -> IO Response
    compareSpec method1@(_, name1) method2@(_, name2) = do
        -- load the methods
        m1@(decls1, mbody1, env1) <- parseMethod method1
        m2@(decls2, mbody2, env2) <- parseMethod method2
        when (decls1 /= decls2) $ fail "inconsistent class declarations"
        -- when (env1 /= env2) $ fail "inconsistent environments"
        putStrLn $ "----PRE---- (" ++ name1 ++ " vs " ++ name2 ++ ")"
        preAns <- determineFormulaEq m1 m2 "pre"
        putStrLn "\n----POST---"
        postAns <- determineFormulaEq m1 m2 "post"
        return $ preAns <> postAns
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    -- Determine the equality of two method's pre/post conditions.
    determineFormulaEq :: MethodDef -> MethodDef -> String -> IO Response
    determineFormulaEq m1@(decls1, mbody1, env1) m2@(decls2, mbody2, env2) name = do
        -- get pre/post condition
        let (e1, e2) = (extractCond m1 name, extractCond m2 name)
        putStrLn $ "e1:\n" ++ prettyPrint e1 ++ "\n\ne2:\n" ++ prettyPrint e2 ++ "\n"
        let (l1, l2) = (javaExpToLExpr e1 env1 decls1, javaExpToLExpr e2 env2 decls2)
        let (l, l') = (lExprPreprocessNull l1, lExprPreprocessNull l2) -- preprocess "a == null" to "isNull(a)"
        putStrLn $ "LogicIR.Pretty 1:\n" ++ prettyLExpr l ++ "\n\nLogicIR.Pretty 2:\n" ++ prettyLExpr l' ++ "\n"
        z3Response <- l `Z3.equivalentTo` l'
        case z3Response of
          Z3.Equivalent      -> return Equivalent
          Z3.NotEquivalent s -> return $ NotEquivalent s
          Z3.Timeout         -> return Timeout -- TODO add QuickCheck on timeout
          _                  -> return Undefined
        where
          extractCond :: MethodDef -> String -> Exp
          extractCond m n = extractExpr (getMethodCalls m n)
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    type MethodDef = ([TypeDecl], Stmt, TypeEnv)
    
    
    -- Takes a Java source file and a method name and returns the class declarations,
    -- Returns the method body and the method's formal parameters.
    
    parseMethod :: (FilePath, String) -> IO MethodDef
    parseMethod (src, name) = do
    
        decls <- parseDecls src
        -- get the method's body (assuming all methods have different names)
    
        let mbody = fromJust $ getMethod decls (Ident name)
        -- get the method's formal parameters:
        let env = getMethodTypeEnv decls (Ident name)
        -- return the relevant data
        return (decls, mbody, env)
    
    Ogilvie, D.H. (Duncan)'s avatar
    Ogilvie, D.H. (Duncan) committed
    -- Get a list of all calls to a method of a specific name from a method definition.
    
    getMethodCalls :: MethodDef -> String -> [MethodInvocation]
    
    Orestis Melkonian's avatar
    Orestis Melkonian committed
    getMethodCalls (_, StmtBlock (Block bs), _) name = mapMaybe extractMethodInv bs
    
        where
            extractMethodInv :: BlockStmt -> Maybe MethodInvocation
            extractMethodInv (BlockStmt (ExpStmt (MethodInv i@(MethodCall (Name [Ident n]) _)))) = if n == name then Just i else Nothing
            extractMethodInv _ = Nothing
    
    
    Ogilvie, D.H. (Duncan)'s avatar
    Ogilvie, D.H. (Duncan) committed
    -- [pre(a), pre(b), pre(c)] -> (a AND b AND c)
    extractExpr :: [MethodInvocation] -> Exp
    extractExpr call = combineExprs $ map (\(MethodCall (Name [Ident _]) [a]) -> a) call
        where combineExprs :: [Exp] -> Exp
    
              combineExprs []     = true
    
              combineExprs [e]    = e
              combineExprs (e:es) = e &* combineExprs es
    
    test9 = testSpec (edslSrc, "swap_spec1") (edslSrc, "swap_spec4") 1000
    
        where edslSrc = "examples/javawlp_edsl/src/nl/uu/javawlp_edsl/Main.java"