3
3
{-# LANGUAGE GADTs #-}
4
4
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5
5
{-# LANGUAGE MultiParamTypeClasses #-}
6
+ {-# LANGUAGE OverloadedStrings #-}
6
7
{-# LANGUAGE RankNTypes #-}
8
+ {-# LANGUAGE TupleSections #-}
7
9
{-# LANGUAGE TypeApplications #-}
8
10
{-# LANGUAGE TypeOperators #-}
9
11
{-# LANGUAGE UndecidableInstances #-}
10
12
module Analysis.Analysis.Exception
11
13
( Exception (.. )
12
14
, ExcSet (.. )
13
15
, exceptionTracing
16
+ , exceptionTracingIndependent
14
17
, fromExceptions
15
18
, var
16
19
, exc
20
+ , str
17
21
, subst
22
+ , nullExcSet
23
+ , freeVariablesForLine
24
+ , exceptionsForLine
25
+ , printExcSet
26
+ , refLines
18
27
-- * Exception tracing analysis
19
28
, ExcC (.. )
20
29
) where
@@ -28,91 +37,158 @@ import Analysis.File
28
37
import Analysis.FlowInsensitive (cacheTerm , convergeTerm )
29
38
import Analysis.Module
30
39
import Analysis.Name
40
+ import Analysis.Reference
31
41
import Control.Algebra
32
42
import Control.Applicative (Alternative (.. ))
43
+ import Control.Carrier.Reader
33
44
import Control.Effect.Labelled
34
45
import Control.Effect.State
46
+ import Control.Monad (unless )
47
+ import Data.Foldable (for_ )
35
48
import qualified Data.Foldable as Foldable
36
49
import Data.Function (fix )
50
+ import qualified Data.IntSet as IntSet
37
51
import qualified Data.Map as Map
38
52
import qualified Data.Set as Set
39
53
import qualified Data.Text as Text
54
+ import qualified Data.Text.IO as Text
55
+ import qualified Source.Source as Source
56
+ import Source.Span
40
57
41
58
-- | Names of exceptions thrown in the guest language and recorded by this analysis.
42
59
--
43
60
-- Not to be confused with exceptions thrown in Haskell itself.
44
- newtype Exception = Exception { exceptionName :: Name }
61
+ data Exception = Exception { exceptionName :: Name , exceptionLines :: IntSet. IntSet }
62
+ deriving (Eq , Ord , Show )
63
+
64
+ data FreeVariable = FreeVariable { freeVariableName :: Name , freeVariableLines :: IntSet. IntSet }
45
65
deriving (Eq , Ord , Show )
46
66
47
67
-- | Sets whose elements are each a variable or an exception.
48
- data ExcSet = ExcSet { freeVariables :: Set. Set Name , exceptions :: Set. Set Exception }
68
+ data ExcSet = ExcSet { freeVariables :: Set. Set FreeVariable , exceptions :: Set. Set Exception , strings :: Set. Set Text. Text }
49
69
deriving (Eq , Ord , Show )
50
70
51
71
instance Semigroup ExcSet where
52
- ExcSet v1 e1 <> ExcSet v2 e2 = ExcSet (v1 <> v2) (e1 <> e2)
72
+ ExcSet v1 e1 s1 <> ExcSet v2 e2 s2 = ExcSet (v1 <> v2) (e1 <> e2) (s1 <> s2 )
53
73
54
74
instance Monoid ExcSet where
55
- mempty = ExcSet mempty mempty
75
+ mempty = ExcSet mempty mempty mempty
56
76
57
77
fromExceptions :: Foldable t => t Exception -> ExcSet
58
- fromExceptions = ExcSet mempty . Set. fromList . Foldable. toList
78
+ fromExceptions es = ExcSet mempty ( Set. fromList ( Foldable. toList es)) mempty
59
79
60
- var :: Name -> ExcSet
61
- var v = ExcSet (Set. singleton v) mempty
80
+ var :: FreeVariable -> ExcSet
81
+ var v = ExcSet (Set. singleton v) mempty mempty
62
82
63
83
exc :: Exception -> ExcSet
64
- exc e = ExcSet mempty (Set. singleton e)
84
+ exc e = ExcSet mempty (Set. singleton e) mempty
85
+
86
+ str :: Text. Text -> ExcSet
87
+ str s = ExcSet mempty mempty (Set. singleton s)
65
88
66
89
subst :: Name -> ExcSet -> ExcSet -> ExcSet
67
- subst name (ExcSet fvs' es') (ExcSet fvs es) = ExcSet (Set. delete name fvs <> fvs') (es <> es')
90
+ -- FIXME: this doesn't handle transitivity at all.
91
+ subst name (ExcSet _ es' _) (ExcSet fvs es ss) = ExcSet fvs'' (es <> es'') ss
92
+ where
93
+ (fvs'', es'') = foldMap combine fvs
94
+ combine fv
95
+ | freeVariableName fv == name = (mempty , Set. map (\ (Exception n _) -> Exception n (freeVariableLines fv)) es')
96
+ | otherwise = (Set. singleton fv, mempty )
97
+
98
+
99
+ nullExcSet :: ExcSet -> Bool
100
+ nullExcSet e = null (freeVariables e) && null (exceptions e)
101
+
102
+ freeVariablesForLine :: Int -> ExcSet -> Set. Set FreeVariable
103
+ freeVariablesForLine l e = Set. filter (\ fv -> IntSet. member l (freeVariableLines fv)) (freeVariables e)
104
+
105
+ exceptionsForLine :: Int -> ExcSet -> Set. Set Exception
106
+ exceptionsForLine l e = Set. filter (\ ex -> IntSet. member l (exceptionLines ex)) (exceptions e)
68
107
108
+ printExcSet :: Source. Source -> ExcSet -> IO ()
109
+ printExcSet src e = for_ (zip [0 .. ] (Source. lines src)) $ \ (i, line) -> do
110
+ Text. putStr (keywords (Text. dropWhileEnd (== ' \n ' ) (Source. toText line)))
111
+ let es = exceptionsForLine i e
112
+ fvs = freeVariablesForLine i e
113
+ unless (null es && null fvs) $ do
114
+ Text. putStr " \ESC [30;1m# "
115
+ Text. putStr (" {" <> union
116
+ ( formatFreeVariables fvs
117
+ <> formatExceptions es ) <> " }" <> reset)
118
+ Text. putStrLn mempty
119
+ where
120
+ keyword k s = Text. intercalate (" \ESC [34;1m" <> k <> reset) (Text. splitOn k s)
121
+ keywords = keyword " raise" . keyword " import" . keyword " def" . keyword " pass"
122
+ union = Text. intercalate " , "
123
+ formatFreeVariables fvs = map (\ fv -> " ?" <> formatName (freeVariableName fv)) (Set. toList fvs)
124
+ formatExceptions excs = map (Text. pack . show . formatName . exceptionName) (Set. toList excs)
125
+ reset = " \ESC [0m"
126
+
127
+ refLines :: Reference -> IntSet. IntSet
128
+ refLines (Reference _ (Span (Pos startLine _) (Pos endLine _))) = IntSet. fromAscList [startLine.. endLine]
69
129
70
130
exceptionTracing
71
131
:: Ord term
72
132
=> ( forall sig m
73
- . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has A. Statement sig m )
133
+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has ( Reader Reference ) sig m , Has A. Statement sig m )
74
134
=> (term -> m ExcSet )
75
135
-> (term -> m ExcSet ) )
76
136
-> [File term ]
77
137
-> (A. MStore ExcSet , [File (Module ExcSet )])
78
- exceptionTracing eval = A. runFiles (runFile eval)
138
+ exceptionTracing eval = run . A. runFiles (runFile eval)
139
+
140
+ exceptionTracingIndependent
141
+ :: Ord term
142
+ => ( forall sig m
143
+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has (Reader Reference ) sig m , Has A. Statement sig m )
144
+ => (term -> m ExcSet )
145
+ -> (term -> m ExcSet ) )
146
+ -> File term
147
+ -> (A. MStore ExcSet , File (Module ExcSet ))
148
+ exceptionTracingIndependent eval = run . A. runStoreState . runFile eval
79
149
80
150
runFile
81
151
:: ( Has (State (A. MStore ExcSet )) sig m
82
152
, Ord term )
83
153
=> ( forall sig m
84
- . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has A. Statement sig m )
154
+ . (Has (Env A. MAddr ) sig m , HasLabelled Store (Store A. MAddr ExcSet ) sig m , Has (Dom ExcSet ) sig m , Has ( Reader Reference ) sig m , Has A. Statement sig m )
85
155
=> (term -> m ExcSet )
86
156
-> (term -> m ExcSet ) )
87
157
-> File term
88
158
-> m (File (Module ExcSet ))
89
- runFile eval = traverse run where
159
+ runFile eval file = traverse run file where
90
160
run
91
161
= A. runStatement result
92
162
. A. runEnv @ ExcSet
163
+ . runReader (fileRef file)
93
164
. convergeTerm (A. runStore @ ExcSet . runExcC . fix (cacheTerm . eval))
94
165
result msgs sets = do
95
166
exports <- gets @ (A. MStore ExcSet ) (fmap Foldable. fold . Map. mapKeys A. getMAddr . A. getMStore)
96
167
let set = Foldable. fold sets
97
168
imports = Set. fromList (map extractImport msgs)
98
- pure (Module (Foldable. foldl' (flip (uncurry subst)) set . Map. toList) imports exports (freeVariables set))
99
- extractImport (A. Import components) = name (Text. intercalate ( Text. pack " ." ) (Foldable. toList components))
169
+ pure (Module (Foldable. foldl' (flip (uncurry subst)) set . Map. toList) imports exports (Set. map freeVariableName ( freeVariables set) ))
170
+ extractImport (A. Import components) = name (Text. intercalate " ." (Foldable. toList components))
100
171
101
172
newtype ExcC m a = ExcC { runExcC :: m a }
102
173
deriving (Alternative , Applicative , Functor , Monad )
103
174
104
- instance (Algebra sig m , Alternative m ) => Algebra (Dom ExcSet :+: sig ) (ExcC m ) where
175
+ instance (Has ( Reader Reference ) sig m , Alternative m ) => Algebra (Dom ExcSet :+: sig ) (ExcC m ) where
105
176
alg hdl sig ctx = ExcC $ case sig of
106
177
L dom -> case dom of
107
- DVar n -> pure $ var n <$ ctx
178
+ DVar n -> do
179
+ lines <- asks refLines
180
+ pure $ var (FreeVariable n lines ) <$ ctx
108
181
DAbs _ b -> runExcC (hdl (b mempty <$ ctx))
109
182
DApp f a -> pure $ f <> Foldable. fold a <$ ctx
110
183
DInt _ -> pure nil
111
184
DUnit -> pure nil
112
185
DBool _ -> pure nil
113
186
DIf c t e -> fmap (mappend c) <$> runExcC (hdl (t <$ ctx) <|> hdl (e <$ ctx))
114
- DString _ -> pure nil
115
- DDie e -> pure $ e <> fromExceptions [Exception n | n <- Set. toList (freeVariables e)] <$ ctx
187
+ DString s -> pure (str (Text. dropAround (== ' "' ) s) <$ ctx)
188
+ t :>>> u -> pure (t <> u <$ ctx)
189
+ DDie e -> do
190
+ lines <- asks refLines
191
+ pure $ e{ strings = mempty } <> fromExceptions [Exception (name n) lines | n <- Set. toList (strings e)] <$ ctx
116
192
where
117
193
nil = (mempty :: ExcSet ) <$ ctx
118
194
0 commit comments