diff --git a/javawlp.cabal b/javawlp.cabal index a4f01e0a99d913e838a7fb31bcd67840c6ea2fa1..eb917c3ed4e2a4a223338fbcb59458d90e6c6122 100644 --- a/javawlp.cabal +++ b/javawlp.cabal @@ -20,28 +20,31 @@ library exposed-modules: Javawlp.Engine.Types , Javawlp.Engine.HelperFunctions , Javawlp.Engine.Folds + -- LIR , LogicIR.Expr , LogicIR.Fold , LogicIR.Parser , LogicIR.Null , LogicIR.Pretty , LogicIR.Frontend.Java - , LogicIR.Backend.Z3 - , LogicIR.Backend.Test - , LogicIR.Backend.ModelGenerator - , LogicIR.Backend.Pretty - , LogicIR.Backend.Null - , LogicIR.Fold , LogicIR.Eval - , LogicIR.Backend.API - , LogicIR.Backend.Model + -- Checker + , SimpleFormulaChecker + -- Z3 + , LogicIR.Backend.Z3.Z3 + , LogicIR.Backend.Z3.API + , LogicIR.Backend.Z3.Model + , LogicIR.Backend.Z3.Pretty + -- QuickCheck + , LogicIR.Backend.QuickCheck.Test + , LogicIR.Backend.QuickCheck.ModelGenerator + -- Java EDSL , Language.Java.Lexer , Language.Java.Parser , Language.Java.Pretty , Language.Java.Syntax , Language.Java.Syntax.Exp , Language.Java.Syntax.Types - , SimpleFormulaChecker build-depends: base >= 4.7 && < 5 , random , parsec @@ -52,8 +55,6 @@ library , mtl , containers default-language: Haskell2010 - -- build-tools: alex, happy - -- other-modules: ModelParser.Lexer, ModelParser.Parser test-suite javawlp-tests type: exitcode-stdio-1.0 diff --git a/src/LogicIR/Backend/ModelGenerator.hs b/src/LogicIR/Backend/QuickCheck/ModelGenerator.hs similarity index 87% rename from src/LogicIR/Backend/ModelGenerator.hs rename to src/LogicIR/Backend/QuickCheck/ModelGenerator.hs index 425cf6ec775a0a44a69f1f6f356c61d26d4e6a4b..ac57a567d55bb6e35e010ebacc80ab9bb0318bcb 100644 --- a/src/LogicIR/Backend/ModelGenerator.hs +++ b/src/LogicIR/Backend/QuickCheck/ModelGenerator.hs @@ -1,18 +1,19 @@ -module LogicIR.Backend.ModelGenerator (generateModel, generateArrayModel, Model, ArrayModel) where +module LogicIR.Backend.QuickCheck.ModelGenerator (generateModel, generateArrayModel, Model, ArrayModel) where -import LogicIR.Expr import Data.List (find, nub, (\\)) import System.Random import Data.Maybe (isNothing, fromJust) import Data.Map (Map) import qualified Data.Map.Lazy as M +import LogicIR.Expr + type Model = [(LExpr, LExpr)] type ArrayModel = Map Var [LConst] -- Generates a constant value for all vars vars in the given list. generateModel :: [LExpr] -> IO Model -generateModel exprs = mapM generateModelEntry exprs +generateModel = mapM generateModelEntry generateArrayModel :: [LExpr] -> IO ArrayModel generateArrayModel es = mapM generateArray es >>= \l -> return (M.fromList l) @@ -31,9 +32,9 @@ generateArrayModel es = mapM generateArray es >>= \l -> return (M.fromList l) This is because otherwise, the evaluation of an LExpr should be changed drastically. After all, How do you evaluate a[0] or a.length if a == null... -} generateModelEntry :: LExpr -> IO (LExpr, LExpr) -generateModelEntry e@(LVar (Var (TPrim t) _)) = do +generateModelEntry e@(LVar (Var (TPrim t) _)) = generatePrimitive t >>= \v -> return (e, v) -generateModelEntry e@(LIsnull (Var (TArray _) _)) = do +generateModelEntry e@(LIsnull (Var (TArray _) _)) = generatePrimitive PBool >>= \v -> return (e, v) generateModelEntry e = error $ "Cannot generate model entry for " ++ show e @@ -54,9 +55,9 @@ generateIntWithBounds b = getStdRandom (randomR b) -- some type primitive type. bounds :: Primitive -> (Int, Int) bounds PBool = (0, 1) -bounds PInt32 = (-10, 10) +bounds PInt = (-10, 10) -- Generates an LExpr given a Primitive type and a value. toLExpr :: Primitive -> Int -> LExpr toLExpr PBool v = LConst $ CBool $ v == 1 -toLExpr PInt32 v = LConst $ CInt $ v \ No newline at end of file +toLExpr PInt v = LConst $ CInt v diff --git a/src/LogicIR/Backend/Test.hs b/src/LogicIR/Backend/QuickCheck/Test.hs similarity index 97% rename from src/LogicIR/Backend/Test.hs rename to src/LogicIR/Backend/QuickCheck/Test.hs index 902110cda16f265ce76243e6f0a39ae507b534fb..9184458c3c9f1b4549f7ad2994da6bf7400fb151 100644 --- a/src/LogicIR/Backend/Test.hs +++ b/src/LogicIR/Backend/QuickCheck/Test.hs @@ -1,12 +1,11 @@ -module LogicIR.Backend.Test (testEquality) where +module LogicIR.Backend.QuickCheck.Test (testEquality) where import LogicIR.Expr import LogicIR.Fold import LogicIR.Eval -import LogicIR.Backend.ModelGenerator -import Data.Maybe (fromMaybe) +import LogicIR.Backend.QuickCheck.ModelGenerator +import Data.Maybe (fromMaybe, isNothing, fromJust) import Data.List (find, nub, (\\)) -import Data.Maybe (isNothing, fromJust) import Data.Map (Map) import qualified Data.Map.Lazy as M @@ -26,7 +25,7 @@ testEquality 0 e1 e2 = do return (True, ([], M.empty)) testEquality n e1 e2 = do (r,m1,m2) <- test e1 e2 - if not r then do + if not r then do return (False, (m1, m2)) else do (rs, model) <- testEquality (n-1) e1 e2 @@ -37,7 +36,7 @@ testEquality n e1 e2 = do -- is used, and the model itself. If the formula, after substitution, cannot -- be evaluated - i.e. there's still a variable in the LExpr - an error will be thrown. testLExpr :: LExpr -> IO (LConst, EvalInfo) -testLExpr e = do +testLExpr e = do let vs@(regularVs, (arrayVs, arrayOpVs), quantVs) = findVariables e let isNullExprs = filter sIsnullExpr arrayOpVs primitivesModel <- generateModel (regularVs ++ isNullExprs) @@ -74,7 +73,7 @@ applyModel e evalInfo = foldLExpr algebra e get arr model = do let res = (M.lookup) arr model - if res == Nothing then do + if res == Nothing then do error $ "Bug in Test.hs made substitution of Array expression impossible. This should never happen. Array: " ++ show arr ++ ", ArrayModel: " ++ show model [] else do diff --git a/src/LogicIR/Backend/API.hs b/src/LogicIR/Backend/Z3/API.hs similarity index 94% rename from src/LogicIR/Backend/API.hs rename to src/LogicIR/Backend/Z3/API.hs index 67fdae92f1a4d99daac28f723c1e9b966f786d1d..d6c834779bfdc71d6ee1b974e3a710deab50250f 100644 --- a/src/LogicIR/Backend/API.hs +++ b/src/LogicIR/Backend/Z3/API.hs @@ -1,4 +1,4 @@ -module LogicIR.Backend.API where +module LogicIR.Backend.Z3.API where import Z3.Monad import Z3.Opts @@ -9,10 +9,10 @@ import Control.Monad.Trans (liftIO) import Data.Maybe (fromJust) import Data.Monoid ((<>)) -import LogicIR.Backend.Model +import LogicIR.Backend.Z3.Model -import LogicIR.Backend.Pretty (showRelevantModel) -import LogicIR.Backend.Z3 +import LogicIR.Backend.Z3.Pretty (showRelevantModel) +import LogicIR.Backend.Z3.Z3 import LogicIR.Expr (LExpr) -- | Z3 Response type. diff --git a/src/LogicIR/Backend/Model.hs b/src/LogicIR/Backend/Z3/Model.hs similarity index 98% rename from src/LogicIR/Backend/Model.hs rename to src/LogicIR/Backend/Z3/Model.hs index 78bcd687c4d39f54a1f906dee4d69f096d0e6ded..cfa8ec62a8f5f8c7a5e337c58a837ad53913a47c 100644 --- a/src/LogicIR/Backend/Model.hs +++ b/src/LogicIR/Backend/Z3/Model.hs @@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -module LogicIR.Backend.Model where +module LogicIR.Backend.Z3.Model where import Data.String import System.IO.Unsafe (unsafePerformIO) @@ -11,8 +11,6 @@ import Text.Parsec.Language import Text.Parsec.String import qualified Text.Parsec.Token as Tokens -import Debug.Trace - data FuncInst = InstVal Integer ModelVal | InstElse ModelVal deriving (Show, Read, Eq, Ord) diff --git a/src/LogicIR/Backend/Pretty.hs b/src/LogicIR/Backend/Z3/Pretty.hs similarity index 97% rename from src/LogicIR/Backend/Pretty.hs rename to src/LogicIR/Backend/Z3/Pretty.hs index 4d96c8f0b71254c1d9fcb74edd5bd974942f85c2..7868b4164df673df6a314090447d4fc161fd16c7 100644 --- a/src/LogicIR/Backend/Pretty.hs +++ b/src/LogicIR/Backend/Z3/Pretty.hs @@ -1,11 +1,12 @@ -module LogicIR.Backend.Pretty (showRelevantModel) where +module LogicIR.Backend.Z3.Pretty (showRelevantModel) where + import Data.List import Data.Maybe import qualified Data.Map as M import Z3.Monad import Z3.Opts -import LogicIR.Backend.Model +import LogicIR.Backend.Z3.Model -- | Function that shows a human-readable model and also highlights potential inconsistencies. showRelevantModel :: Z3Model -> IO () diff --git a/src/LogicIR/Backend/Z3.hs b/src/LogicIR/Backend/Z3/Z3.hs similarity index 98% rename from src/LogicIR/Backend/Z3.hs rename to src/LogicIR/Backend/Z3/Z3.hs index e30794dbf09881bfdb1e45caf33748f330801b73..f03babc8e6717d85814f5e7c6ce6850bc47fac06 100644 --- a/src/LogicIR/Backend/Z3.hs +++ b/src/LogicIR/Backend/Z3/Z3.hs @@ -1,5 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} -module LogicIR.Backend.Z3 where +module LogicIR.Backend.Z3.Z3 where import Z3.Monad import Control.Monad.Trans (liftIO) diff --git a/src/LogicIR/Eval.hs b/src/LogicIR/Eval.hs index 939e89b4f072363237ff3d12066e77046c724ece..5fc82fc7ae08d3074a6d9d364184ae8882d72567 100644 --- a/src/LogicIR/Eval.hs +++ b/src/LogicIR/Eval.hs @@ -10,7 +10,7 @@ eval :: LExpr -> LConst eval = foldLExpr evalAlgebra evalIfPossible :: LExpr -> LExpr -evalIfPossible e = if evalPossible e then (LConst (eval e)) else e +evalIfPossible e = if evalPossible e then LConst (eval e) else e -- Returns True if an LExpr only contains constants and no variables whatsoever. evalPossible :: LExpr -> Bool @@ -33,21 +33,14 @@ evalAlgebra :: LExprAlgebra LConst evalAlgebra = (cnst, var, uni, bin, iff, quant, arr, snull, len) where cnst b@(CBool _) = b cnst i@(CInt _) = i - uni NNeg (CInt i) = CInt (-i) - uni NNot (CInt i) = CInt (-i - 1) uni LNot (CBool b) = CBool (not b) - - bin le o re = evalBin le o re - - iff ge e1 e2 = if ge == (CBool True) then e1 else e2 - + bin = evalBin + iff ge e1 e2 = if ge == CBool True then e1 else e2 -- This possibly needs to be changed. len v = CInt 10 -- error "Array length cannot yet be evaluated." - snull (Var (TPrim _) _) = CBool False -- Primitive type is never null. snull (Var (TArray _) _) = error "You can't call eval on an LExpr that has an (LIsnull someArrayVar) that wasn't converted to a boolean first." - -- Things that should never happen. quant _ _ e1 e2 = error "Quantifiers cannot be evaluated and should be replaced using LogicIR.Rewrite.replaceQuantifiers." arr v a = error "You can't call eval on an LExpr that contains uninstantiated arrays." @@ -61,9 +54,9 @@ evalBin (CInt l) CGreater (CInt r) = CBool (l > r) -- Logical operators evalBin (CBool l) LAnd (CBool r) = CBool (l && r) evalBin (CBool l) LOr (CBool r) = CBool (l || r) -evalBin (CBool l) LImpl (CBool r) = CBool ((not l) || (l && r)) +evalBin (CBool l) LImpl (CBool r) = CBool (not l || (l && r)) -- Numerical operators -evalBin (CInt l) o (CInt r) = CInt ((convert o) l r) +evalBin (CInt l) o (CInt r) = CInt (convert o l r) -- Both NShr and NShl are evaluated using the Data.Bits.shift function, -- where a right shift is achieved by inverting the r integer. where convert NAdd = (+) @@ -71,8 +64,3 @@ evalBin (CInt l) o (CInt r) = CInt ((convert o) l r) convert NMul = (*) convert NDiv = div convert NRem = mod - convert NShl = shiftL - convert NShr = shiftR - convert NAnd = (.&.) - convert NOr = (.|.) - convert NXor = xor diff --git a/src/LogicIR/Pretty.hs b/src/LogicIR/Pretty.hs index c24fb1fa5376c302dd65008530f3c6781764efce..10df1c7d727b4f37b3288c4fa986c8d474e26837 100644 --- a/src/LogicIR/Pretty.hs +++ b/src/LogicIR/Pretty.hs @@ -7,7 +7,6 @@ import qualified Data.Map as M import Z3.Monad import Z3.Opts -import LogicIR.Backend.Model import LogicIR.Expr import LogicIR.Fold diff --git a/src/SimpleFormulaChecker.hs b/src/SimpleFormulaChecker.hs index 518329c90ce73b5bca4b823c4d08cb27b0e9151d..db2c459551badf1de2755c7e73d3a9422b7d9f16 100644 --- a/src/SimpleFormulaChecker.hs +++ b/src/SimpleFormulaChecker.hs @@ -12,14 +12,12 @@ import Language.Java.Parser import Language.Java.Pretty import Language.Java.Syntax -import qualified LogicIR.Backend.API as Z3 +import qualified LogicIR.Backend.Z3.API as Z3 import LogicIR.Expr import LogicIR.Eval -import LogicIR.Frontend.Java -import LogicIR.Backend.Z3 -import LogicIR.Backend.Test -import LogicIR.Backend.Pretty -import LogicIR.Backend.Null +import LogicIR.Backend.Z3.Z3 +import LogicIR.Backend.QuickCheck.Test +import LogicIR.Backend.Z3.Pretty import LogicIR.Frontend.Java (javaExpToLExpr) import LogicIR.Null (lExprPreprocessNull) @@ -98,5 +96,32 @@ extractExpr call = combineExprs $ map (\(MethodCall (Name [Ident _]) [a]) -> a) 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" +parse :: (FilePath, String) -> (FilePath, String) -> IO (MethodDef, MethodDef) +parse method1@(_, name1) method2@(_, name2) = do + -- load the methods + m1@(decls1, mbody1, env1) <- parseMethod method1 + m2@(decls2, mbody2, env2) <- parseMethod method2 + when (env1 /= env2) $ fail "inconsistent method parameters" + when (decls1 /= decls2) $ fail "inconsistent class declarations (TODO)" + return (m1, m2) + +methodDefToLExpr :: MethodDef -> MethodDef -> String -> (LExpr, LExpr) +methodDefToLExpr m1@(decls1, _, env1) m2@(decls2, _, env2) name = do + -- get pre/post condition + let (e1, e2) = (extractCond m1 name, extractCond m2 name) + let (lExpr1', lExpr2') = (javaExpToLExpr e1 env1 decls1, javaExpToLExpr e2 env2 decls2) + -- preprocess "a == null" to "isNull(a)" + let (lExpr1, lExpr2) = (lExprPreprocessNull lExpr1', lExprPreprocessNull lExpr2') + (lExpr1, lExpr2) + where extractCond m n = extractExpr $ getMethodCalls m n + +testSpec :: (FilePath, String) -> (FilePath, String) -> Int -> IO Bool +testSpec method1@(_, name1) method2@(_, name2) n = do + (m1, m2) <- parse method1 method2 + putStrLn $ "----PRE---- (" ++ name1 ++ " vs " ++ name2 ++ ")" + let (lExpr1, lExpr2) = methodDefToLExpr m1 m2 "pre" + (preAns, counterModel) <- testEquality n lExpr1 lExpr2 + putStrLn "\n----POST---" + let (lExpr1, lExpr2) = methodDefToLExpr m1 m2 "post" + (postAns, counterModel) <- testEquality n lExpr1 lExpr2 + return $ preAns && postAns diff --git a/test/Spec.hs b/test/Spec.hs index 4b11dcc0ba1dc092c9c7111beea95fd1cedcfd34..d7ec9c8b8f806872774d6e554ceef7c1bb011f96 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -13,6 +13,7 @@ main = defaultMain ("LIR_PARSER", parserTests) , ("Z3_MODEL", modelTests) , ("EXAMPLES", examples) + , ("AUTOMATED_TESTING_CHECK", testingModuleTests) , ("EQUIV_REAL", genEquivTests "examples/test_equiv/Reals.java") , ("EQUIV_ARRAY", genEquivTests "examples/test_equiv/Arrays.java") ] diff --git a/test/TIRTest.hs b/test/TIRTest.hs index 448e8921f5c2ea0bb75baebb3e98bdae2e81dbcf..22709623f999225bd7e21b51500e51fc5f2ae212 100644 --- a/test/TIRTest.hs +++ b/test/TIRTest.hs @@ -31,4 +31,4 @@ testingModuleTests = , (neq "sorted3" "test2" 100) , (neq "sorted3" "sorted4" 500) , (neq "sorted1" "sorted4" 100) - ] \ No newline at end of file + ] diff --git a/test/TZ3Model.hs b/test/TZ3Model.hs index 7fb12985236c4b64da189b1b34a200867b8990ed..924615447f1ef7fa856b950efc506ff51dd88022 100644 --- a/test/TZ3Model.hs +++ b/test/TZ3Model.hs @@ -3,8 +3,7 @@ import System.IO.Unsafe (unsafePerformIO) import System.IO.Silently (silence) import Test.HUnit -import LogicIR.Backend.Model -import Debug.Trace +import LogicIR.Backend.Z3.Model (~~) model ast = runP modelP model @?= ast