1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
|
module CASS.WorkerFunctions where
import Data.IORef
import Data.List ( partition )
import System.CPUTime ( getCPUTime )
import Analysis.Files
import Analysis.Logging ( debugMessage, debugString )
import Analysis.Types ( Analysis(..), isSimpleAnalysis, isCombinedAnalysis
, analysisName, startValue, isFunctionAnalysis, isTypeAnalysis)
import Analysis.ProgInfo ( ProgInfo, combineProgInfo, emptyProgInfo
, publicProgInfo, lookupProgInfo, lists2ProgInfo
, equalProgInfo, publicListFromProgInfo, showProgInfo )
import Data.Map as Map
import FlatCurry.Types
import FlatCurry.Goodies
import Data.SCC ( scc )
import Data.Set.RBTree as Set ( SetRBT, member, empty, insert, null )
import RW.Base
import CASS.Configuration
import CASS.FlatCurryDependency ( callsDirectly, dependsDirectlyOnTypes )
import CPM.Query.Main (askCurryInfoCmd)
import qualified CPM.Query.Options as CPMQuery ( CurryEntity(..) )
type ProgInfoStore a = [(String,ProgInfo a)]
newProgInfoStoreRef :: IO (IORef (ProgInfoStore _))
newProgInfoStoreRef = newIORef []
analysisClient :: (Eq a, Show a, Read a, ReadWrite a) =>
Analysis a -> CConfig -> [String] -> IO ()
analysisClient analysis cconfig modnames = do
store <- newIORef []
let fpmethod = fixpointMethod cconfig
mapM_ (analysisClientWithStore cconfig store analysis fpmethod) modnames
analysisClientWithStore :: (Eq a, Show a, Read a, ReadWrite a)
=> CConfig -> IORef (ProgInfoStore a) -> Analysis a
-> String -> String -> IO ()
analysisClientWithStore cconfig store analysis fpmethod moduleName = do
prog <- readNewestFlatCurry moduleName
let progimports = progImports prog
importList = if withPrelude cconfig
then progimports
else filter (/="Prelude") progimports
ananame = analysisName analysis
importInfos <-
if isSimpleAnalysis analysis
then return emptyProgInfo
else getInterfaceInfosWS cconfig store (analysisName analysis) importList
let anaModName = ananame ++ "/" ++ moduleName
debugMessage dl 1 $ "Starting analysis for " ++ anaModName ++ "..."
starttime <- getCPUTime
startvals <- getStartValues analysis prog
curryInfoResult <-
if useCurryInfo cconfig &&
(isFunctionAnalysis analysis || isTypeAnalysis analysis)
then do let entkind = if isTypeAnalysis analysis then CPMQuery.Type
else CPMQuery.Operation
debugMessage dl 1 $ "\nUse CURRYINFO for " ++
moduleName ++ " / " ++ "cass-" ++ ananame
res <- askCurryInfoCmd moduleName entkind ("cass-" ++ ananame)
debugMessage dl 3 $ "Result from CURRYINFO:\n" ++ show res
return res
else return Nothing
result <- maybe
(debugMessage dl 1 ("\nAnalyze by CASS: " ++ moduleName ++
" / " ++ ananame) >>
if isCombinedAnalysis analysis
then execCombinedAnalysis cconfig analysis prog importInfos
startvals moduleName fpmethod
else runAnalysis cconfig analysis prog importInfos startvals fpmethod
)
(\i -> return (lists2ProgInfo (i, [])))
(curryInfoResult >>= mapM (\(qn, s) -> fmap ((,) qn) (safeRead s)))
storeAnalysisResult dl ananame moduleName result
stoptime <- getCPUTime
debugMessage dl 1 $ "Analysis time for " ++ anaModName ++ ": " ++
show (stoptime - starttime) ++ " msecs"
loadinfos <- readIORef store
writeIORef store ((moduleName,publicProgInfo result):loadinfos)
where
dl = debugLevel cconfig
safeRead s = case readsPrec 0 s of [(x, "")] -> Just x
_ -> Nothing
getInterfaceInfosWS :: (Read a, ReadWrite a) => CConfig
-> IORef (ProgInfoStore a) -> String
-> [String] -> IO (ProgInfo a)
getInterfaceInfosWS _ _ _ [] = return emptyProgInfo
getInterfaceInfosWS cc store anaName (mod:mods) = do
loadinfos <- readIORef store
modInfo <- maybe (loadAndStoreAnalysis loadinfos) return
(Prelude.lookup mod loadinfos)
modsInfo <- getInterfaceInfosWS cc store anaName mods
return (combineProgInfo modInfo modsInfo)
where
loadAndStoreAnalysis loadinfos = do
info <- loadPublicAnalysis (debugLevel cc) anaName mod
writeIORef store ((mod,info):loadinfos)
return info
getStartValues :: Analysis a -> Prog -> IO [(QName,a)]
getStartValues analysis prog =
if isSimpleAnalysis analysis
then return []
else do
let startvals = case analysis of
DependencyFuncAnalysis _ _ _ ->
map (\func->(funcName func,startValue analysis))
(progFuncs prog)
CombinedDependencyFuncAnalysis _ _ _ _ _ ->
map (\func->(funcName func,startValue analysis))
(progFuncs prog)
DependencyTypeAnalysis _ _ _ ->
map (\typeDecl->(typeName typeDecl,startValue analysis))
(progTypes prog)
CombinedDependencyTypeAnalysis _ _ _ _ _ ->
map (\typeDecl->(typeName typeDecl,startValue analysis))
(progTypes prog)
_ -> error "Internal error in WorkerFunctions.getStartValues"
return startvals
funcInfos2ProgInfo :: Prog -> [(QName,a)] -> ProgInfo a
funcInfos2ProgInfo prog infos = lists2ProgInfo $
map2 (\fdecl -> let fname = funcName fdecl
in (fname, maybe (lookupError "funcInfos2ProgInfo" fname) id
(Prelude.lookup fname infos)))
(partition isVisibleFunc (progFuncs prog))
typeInfos2ProgInfo :: Prog -> [(QName,a)] -> ProgInfo a
typeInfos2ProgInfo prog infos = lists2ProgInfo $
map2 (\tdecl -> let tname = typeName tdecl
in (tname, maybe (lookupError "typeInfos2ProgInfo" tname) id
(Prelude.lookup tname infos)))
(partition isVisibleType (progTypes prog))
map2 :: (a -> b) -> ([a], [a]) -> ([b], [b])
map2 f (xs,ys) = (map f xs, map f ys)
updateList :: Eq a => [(a,b)] -> [(a,b)] -> [(a,b)]
updateList [] oldList = oldList
updateList ((key,newValue):newList) oldList =
updateList newList (updateValue (key,newValue) oldList)
updateValue :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
updateValue _ [] = []
updateValue (key1,newValue) ((key2,value2):list) =
if key1==key2 then (key1,newValue):list
else (key2,value2):(updateValue (key1,newValue) list)
execCombinedAnalysis ::
(Eq a, Read a) => CConfig -> Analysis a -> Prog -> ProgInfo a -> [(QName,a)]
-> String -> String -> IO (ProgInfo a)
execCombinedAnalysis cc analysis prog importInfos startvals moduleName
fpmethod =
case analysis of
CombinedSimpleFuncAnalysis _ ananame _ runWithBaseAna -> do
anaFunc <- runWithBaseAna moduleName
runAnalysis cc (SimpleFuncAnalysis ananame anaFunc)
prog importInfos startvals fpmethod
CombinedSimpleTypeAnalysis _ ananame _ runWithBaseAna -> do
anaFunc <- runWithBaseAna moduleName
runAnalysis cc (SimpleTypeAnalysis ananame anaFunc)
prog importInfos startvals fpmethod
CombinedDependencyFuncAnalysis _ ananame _ startval runWithBaseAna -> do
anaFunc <- runWithBaseAna moduleName
runAnalysis cc (DependencyFuncAnalysis ananame startval anaFunc)
prog importInfos startvals fpmethod
CombinedDependencyTypeAnalysis _ ananame _ startval runWithBaseAna -> do
anaFunc <- runWithBaseAna moduleName
runAnalysis cc (DependencyTypeAnalysis ananame startval anaFunc)
prog importInfos startvals fpmethod
_ -> error "Internal error in WorkerFunctions.execCombinedAnalysis"
runAnalysis :: (Eq a, Read a) => CConfig -> Analysis a -> Prog -> ProgInfo a
-> [(QName,a)] -> String -> IO (ProgInfo a)
runAnalysis cconfig analysis prog importInfos startvals fpmethod = do
deflts <- loadDefaultAnalysisValues dl (analysisName analysis) (progName prog)
let defaultFuncs =
updProgFuncs (filter (\fd -> funcName fd `elem` map fst deflts)) prog
definedFuncs =
updProgFuncs (filter (\fd -> funcName fd `notElem` map fst deflts)) prog
defaultTypes =
updProgTypes (filter (\fd -> typeName fd `elem` map fst deflts)) prog
definedTypes =
updProgTypes (filter (\fd -> typeName fd `notElem` map fst deflts)) prog
let (progWithoutDefaults,defaultproginfo) = case analysis of
SimpleFuncAnalysis _ _ ->
(definedFuncs, funcInfos2ProgInfo defaultFuncs deflts)
SimpleTypeAnalysis _ _ ->
(definedTypes, typeInfos2ProgInfo defaultTypes deflts)
SimpleConstructorAnalysis _ _ ->
if Prelude.null deflts then (prog,emptyProgInfo)
else error "SimpleConstructorAnalysis with default values!"
DependencyFuncAnalysis _ _ _ ->
(definedFuncs, funcInfos2ProgInfo defaultFuncs deflts)
DependencyTypeAnalysis _ _ _ ->
(definedTypes, typeInfos2ProgInfo defaultTypes deflts)
SimpleModuleAnalysis _ _ ->
if Prelude.null deflts then (definedFuncs, emptyProgInfo)
else error defaultNotEmptyError
DependencyModuleAnalysis _ _ ->
if Prelude.null deflts then (definedFuncs, emptyProgInfo)
else error defaultNotEmptyError
_ -> error "Internal error in WorkerFunctions.runAnalysis"
let result = executeAnalysis analysis progWithoutDefaults
(combineProgInfo importInfos defaultproginfo)
startvals fpmethod
return $ combineProgInfo defaultproginfo result
where
dl = debugLevel cconfig
defaultNotEmptyError = "Default analysis information for analysis '" ++
analysisName analysis ++ "' and module '" ++
progName prog ++ "' not empty!"
executeAnalysis :: Eq a => Analysis a -> Prog -> ProgInfo a -> [(QName,a)]
-> String
-> ProgInfo a
executeAnalysis (SimpleModuleAnalysis _ anaFunc) prog _ _ _ =
let pname = progName prog
in lists2ProgInfo ([((pname,pname), anaFunc prog)], [])
executeAnalysis (DependencyModuleAnalysis _ anaFunc) prog impproginfos _ _ =
let pname = progName prog
importinfos = map (\ (qn,a) -> (fst qn,a))
(publicListFromProgInfo impproginfos)
in lists2ProgInfo ([((pname,pname), anaFunc prog importinfos)], [])
executeAnalysis (SimpleFuncAnalysis _ anaFunc) prog _ _ _ =
(lists2ProgInfo . map2 (\func -> (funcName func, anaFunc func))
. partition isVisibleFunc . progFuncs) prog
executeAnalysis (SimpleTypeAnalysis _ anaFunc) prog _ _ _ =
(lists2ProgInfo . map2 (\typ -> (typeName typ,anaFunc typ))
. partition isVisibleType . progTypes) prog
executeAnalysis (SimpleConstructorAnalysis _ anaFunc) prog _ _ _ =
(lists2ProgInfo
. map2 (\ (cdecl,tdecl) -> (consName cdecl, anaFunc cdecl tdecl))
. partition isVisibleCons
. concatMap (\t -> map (\c -> (c,t)) (consDeclsOfType t))
. progTypes) prog
where
isVisibleCons (consDecl,_) = consVisibility consDecl == Public
executeAnalysis (DependencyFuncAnalysis _ _ anaFunc) prog
importInfos startvals fpmethod = case fpmethod of
"simple" ->
let declsWithDeps = map2 addCalledFunctions
(partition isVisibleFunc (progFuncs prog))
startinfo = funcInfos2ProgInfo prog startvals
in simpleIteration anaFunc funcName declsWithDeps importInfos startinfo
"wlist" ->
let declsWithDeps = map addCalledFunctions (progFuncs prog)
in funcInfos2ProgInfo prog $ toList $
wlIteration anaFunc funcName declsWithDeps [] (Set.empty (<))
importInfos (fromList startvals)
"wlistscc" ->
let declsWithDeps = map addCalledFunctions (progFuncs prog)
sccDecls = scc ((:[]) . funcName . fst) snd declsWithDeps
in funcInfos2ProgInfo prog $ toList $
foldr (\scc sccstartvals ->
wlIteration anaFunc funcName scc [] (Set.empty (<))
importInfos sccstartvals)
(fromList startvals)
(reverse sccDecls)
_ -> error unknownFixpointMessage
executeAnalysis (DependencyTypeAnalysis _ _ anaType) prog
importInfos startvals fpmethod = case fpmethod of
"simple" ->
let declsWithDeps = map2 addUsedTypes
(partition isVisibleType (progTypes prog))
startinfo = typeInfos2ProgInfo prog startvals
in simpleIteration anaType typeName declsWithDeps importInfos startinfo
"wlist" ->
let declsWithDeps = map addUsedTypes (progTypes prog)
in typeInfos2ProgInfo prog $ toList $
wlIteration anaType typeName declsWithDeps [] (Set.empty (<))
importInfos (fromList startvals)
"wlistscc" ->
let declsWithDeps = map addUsedTypes (progTypes prog)
sccDecls = scc ((:[]) . typeName . fst) snd declsWithDeps
in typeInfos2ProgInfo prog $ toList $
foldr (\scc sccstartvals ->
wlIteration anaType typeName scc [] (Set.empty (<))
importInfos sccstartvals)
(fromList startvals)
(reverse sccDecls)
_ -> error unknownFixpointMessage
executeAnalysis (CombinedSimpleFuncAnalysis _ _ _ _) _ _ _ _ =
error "Internal error in WorkerFunctions.executeAnalysis"
executeAnalysis (CombinedSimpleTypeAnalysis _ _ _ _) _ _ _ _ =
error "Internal error in WorkerFunctions.executeAnalysis"
executeAnalysis (CombinedDependencyFuncAnalysis _ _ _ _ _) _ _ _ _ =
error "Internal error in WorkerFunctions.executeAnalysis"
executeAnalysis (CombinedDependencyTypeAnalysis _ _ _ _ _) _ _ _ _ =
error "Internal error in WorkerFunctions.executeAnalysis"
unknownFixpointMessage :: String
unknownFixpointMessage = "Unknown value for 'fixpoint' in configuration file!"
addCalledFunctions :: FuncDecl -> (FuncDecl,[QName])
addCalledFunctions func = (func, callsDirectly func)
addUsedTypes :: TypeDecl -> (TypeDecl,[QName])
addUsedTypes tdecl = (tdecl, dependsDirectlyOnTypes tdecl)
consDeclsOfType :: TypeDecl -> [ConsDecl]
consDeclsOfType (Type _ _ _ consDecls) = consDecls
consDeclsOfType (TypeSyn _ _ _ _) = []
consDeclsOfType (TypeNew _ _ _ (NewCons qn vis te)) = [Cons qn 1 vis [te]]
simpleIteration :: Eq a => (t -> [(QName,a)] -> a) -> (t -> QName)
-> ([(t,[QName])],[(t,[QName])])
-> ProgInfo a -> ProgInfo a -> ProgInfo a
simpleIteration analysis nameOf declsWithDeps importInfos currvals =
let completeProgInfo = combineProgInfo currvals importInfos
newvals =
map2 (\ (decl,calls) ->
(nameOf decl,
analysis decl
(map (\qn -> (qn,maybe (lookupError "simpleIteration" qn) id
(lookupProgInfo qn completeProgInfo)))
calls)))
declsWithDeps
newproginfo = lists2ProgInfo newvals
in if equalProgInfo currvals newproginfo
then currvals
else simpleIteration analysis nameOf declsWithDeps importInfos newproginfo
wlIteration :: (Eq a, Eq b) => (a -> [(QName,b)] -> b) -> (a -> QName)
-> [(a,[QName])] -> [(a,[QName])] -> SetRBT QName
-> ProgInfo b -> Map QName b -> Map QName b
wlIteration analysis nameOf [] alldecls changedEntities importInfos currvals =
if Set.null changedEntities
then currvals
else
let (declsToDo,declsDone) =
partition (\ (_,calls) -> any (`Set.member` changedEntities) calls)
alldecls
in wlIteration analysis nameOf declsToDo declsDone (Set.empty (<))
importInfos currvals
wlIteration analysis nameOf (decldeps@(decl,calls):decls) declsDone
changedEntities importInfos currvals =
let decname = nameOf decl
lookupVal qn = maybe (maybe (lookupError "wlIteration" qn) id
(Map.lookup qn currvals)) id
(lookupProgInfo qn importInfos)
oldval = lookupVal decname
newval = analysis decl (map (\qn -> (qn, lookupVal qn)) calls)
in if oldval==newval
then wlIteration analysis nameOf decls (decldeps:declsDone)
changedEntities importInfos currvals
else wlIteration analysis nameOf decls (decldeps:declsDone)
(Set.insert decname changedEntities) importInfos
(Map.adjust (const newval) decname currvals)
lookupError :: String -> QName -> _
lookupError s qn =
error $ "Internal error in CASS." ++ s ++ ": " ++
showQName qn ++ " not found."
isVisibleFunc :: FuncDecl -> Bool
isVisibleFunc funcDecl = funcVisibility funcDecl == Public
isVisibleType :: TypeDecl -> Bool
isVisibleType typeDecl = typeVisibility typeDecl == Public
|