-
Notifications
You must be signed in to change notification settings - Fork 658
/
Copy pathGenerate.hs
211 lines (152 loc) · 6.47 KB
/
Generate.hs
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
{-# LANGUAGE BangPatterns #-}
module Generate
( debug
, dev
, prod
, repl
)
where
import Prelude hiding (cycle, print)
import Control.Concurrent (MVar, forkIO, newEmptyMVar, newMVar, putMVar, readMVar)
import Control.Monad (liftM2)
import qualified Data.ByteString.Builder as B
import Data.Map ((!))
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Name as N
import qualified Data.NonEmptyList as NE
import qualified AST.Optimized as Opt
import qualified Build
import qualified Elm.Compiler.Type.Extract as Extract
import qualified Elm.Details as Details
import qualified Elm.Interface as I
import qualified Elm.ModuleName as ModuleName
import qualified Elm.Package as Pkg
import qualified File
import qualified Generate.JavaScript as JS
import qualified Generate.Mode as Mode
import qualified Nitpick.Debug as Nitpick
import qualified Reporting.Exit as Exit
import qualified Reporting.Task as Task
import qualified Stuff
-- NOTE: This is used by Make, Repl, and Reactor right now. But it may be
-- desireable to have Repl and Reactor to keep foreign objects in memory
-- to make things a bit faster?
-- GENERATORS
type Task a =
Task.Task Exit.Generate a
debug :: FilePath -> Details.Details -> Build.Artifacts -> Task B.Builder
debug root details (Build.Artifacts pkg ifaces roots modules) =
do loading <- loadObjects root details modules
types <- loadTypes root ifaces modules
objects <- finalizeObjects loading
let mode = Mode.Dev (Just types)
let graph = objectsToGlobalGraph objects
let mains = gatherMains pkg objects roots
return $ JS.generate mode graph mains
dev :: FilePath -> Details.Details -> Build.Artifacts -> Task B.Builder
dev root details (Build.Artifacts pkg _ roots modules) =
do objects <- finalizeObjects =<< loadObjects root details modules
let mode = Mode.Dev Nothing
let graph = objectsToGlobalGraph objects
let mains = gatherMains pkg objects roots
return $ JS.generate mode graph mains
prod :: FilePath -> Details.Details -> Build.Artifacts -> Task B.Builder
prod root details (Build.Artifacts pkg _ roots modules) =
do objects <- finalizeObjects =<< loadObjects root details modules
checkForDebugUses objects
let graph = objectsToGlobalGraph objects
let mode = Mode.Prod (Mode.shortenFieldNames graph)
let mains = gatherMains pkg objects roots
return $ JS.generate mode graph mains
repl :: FilePath -> Details.Details -> Bool -> Build.ReplArtifacts -> N.Name -> Task B.Builder
repl root details ansi (Build.ReplArtifacts home modules localizer annotations) name =
do objects <- finalizeObjects =<< loadObjects root details modules
let graph = objectsToGlobalGraph objects
return $ JS.generateForRepl ansi localizer graph home name (annotations ! name)
-- CHECK FOR DEBUG
checkForDebugUses :: Objects -> Task ()
checkForDebugUses (Objects _ locals) =
case Map.keys (Map.filter Nitpick.hasDebugUses locals) of
[] -> return ()
m:ms -> Task.throw (Exit.GenerateCannotOptimizeDebugValues m ms)
-- GATHER MAINS
gatherMains :: Pkg.Name -> Objects -> NE.List Build.Root -> Map.Map ModuleName.Canonical Opt.Main
gatherMains pkg (Objects _ locals) roots =
Map.fromList $ Maybe.mapMaybe (lookupMain pkg locals) (NE.toList roots)
lookupMain :: Pkg.Name -> Map.Map ModuleName.Raw Opt.LocalGraph -> Build.Root -> Maybe (ModuleName.Canonical, Opt.Main)
lookupMain pkg locals root =
let
toPair name (Opt.LocalGraph maybeMain _ _) =
(,) (ModuleName.Canonical pkg name) <$> maybeMain
in
case root of
Build.Inside name -> toPair name =<< Map.lookup name locals
Build.Outside name _ g -> toPair name g
-- LOADING OBJECTS
data LoadingObjects =
LoadingObjects
{ _foreign_mvar :: MVar (Maybe Opt.GlobalGraph)
, _local_mvars :: Map.Map ModuleName.Raw (MVar (Maybe Opt.LocalGraph))
}
loadObjects :: FilePath -> Details.Details -> [Build.Module] -> Task LoadingObjects
loadObjects root details modules =
Task.io $
do mvar <- Details.loadObjects root details
mvars <- traverse (loadObject root) modules
return $ LoadingObjects mvar (Map.fromList mvars)
loadObject :: FilePath -> Build.Module -> IO (ModuleName.Raw, MVar (Maybe Opt.LocalGraph))
loadObject root modul =
case modul of
Build.Fresh name _ graph ->
do mvar <- newMVar (Just graph)
return (name, mvar)
Build.Cached name _ _ ->
do mvar <- newEmptyMVar
_ <- forkIO $ putMVar mvar =<< File.readBinary (Stuff.elmo root name)
return (name, mvar)
-- FINALIZE OBJECTS
data Objects =
Objects
{ _foreign :: Opt.GlobalGraph
, _locals :: Map.Map ModuleName.Raw Opt.LocalGraph
}
finalizeObjects :: LoadingObjects -> Task Objects
finalizeObjects (LoadingObjects mvar mvars) =
Task.eio id $
do result <- readMVar mvar
results <- traverse readMVar mvars
case liftM2 Objects result (sequence results) of
Just loaded -> return (Right loaded)
Nothing -> return (Left Exit.GenerateCannotLoadArtifacts)
objectsToGlobalGraph :: Objects -> Opt.GlobalGraph
objectsToGlobalGraph (Objects globals locals) =
foldr Opt.addLocalGraph globals locals
-- LOAD TYPES
loadTypes :: FilePath -> Map.Map ModuleName.Canonical I.DependencyInterface -> [Build.Module] -> Task Extract.Types
loadTypes root ifaces modules =
Task.eio id $
do mvars <- traverse (loadTypesHelp root) modules
let !foreigns = Extract.mergeMany (Map.elems (Map.mapWithKey Extract.fromDependencyInterface ifaces))
results <- traverse readMVar mvars
case sequence results of
Just ts -> return (Right (Extract.merge foreigns (Extract.mergeMany ts)))
Nothing -> return (Left Exit.GenerateCannotLoadArtifacts)
loadTypesHelp :: FilePath -> Build.Module -> IO (MVar (Maybe Extract.Types))
loadTypesHelp root modul =
case modul of
Build.Fresh name iface _ ->
newMVar (Just (Extract.fromInterface name iface))
Build.Cached name _ ciMVar ->
do cachedInterface <- readMVar ciMVar
case cachedInterface of
Build.Unneeded ->
do mvar <- newEmptyMVar
_ <- forkIO $
do maybeIface <- File.readBinary (Stuff.elmi root name)
putMVar mvar (Extract.fromInterface name <$> maybeIface)
return mvar
Build.Loaded iface ->
newMVar (Just (Extract.fromInterface name iface))
Build.Corrupted ->
newMVar Nothing