adamc@607
|
1 (* Copyright (c) 2009, Adam Chlipala
|
adamc@607
|
2 * All rights reserved.
|
adamc@607
|
3 *
|
adamc@607
|
4 * Redistribution and use in source and binary forms, with or without
|
adamc@607
|
5 * modification, are permitted provided that the following conditions are met:
|
adamc@607
|
6 *
|
adamc@607
|
7 * - Redistributions of source code must retain the above copyright notice,
|
adamc@607
|
8 * this list of conditions and the following disclaimer.
|
adamc@607
|
9 * - Redistributions in binary form must reproduce the above copyright notice,
|
adamc@607
|
10 * this list of conditions and the following disclaimer in the documentation
|
adamc@607
|
11 * and/or other materials provided with the distribution.
|
adamc@607
|
12 * - The names of contributors may not be used to endorse or promote products
|
adamc@607
|
13 * derived from this software without specific prior written permission.
|
adamc@607
|
14 *
|
adamc@607
|
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
adamc@607
|
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
adamc@607
|
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
adamc@607
|
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
adamc@607
|
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
adamc@607
|
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
adamc@607
|
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
adamc@607
|
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
adamc@607
|
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
adamc@607
|
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
adamc@607
|
25 * POSSIBILITY OF SUCH DAMAGE.
|
adamc@607
|
26 *)
|
adamc@607
|
27
|
adamc@607
|
28 structure Rpcify :> RPCIFY = struct
|
adamc@607
|
29
|
adamc@607
|
30 open Core
|
adamc@607
|
31
|
adamc@607
|
32 structure U = CoreUtil
|
adamc@607
|
33 structure E = CoreEnv
|
adamc@607
|
34
|
adamc@954
|
35 fun multiLiftExpInExp n e =
|
adamc@954
|
36 if n = 0 then
|
adamc@954
|
37 e
|
adamc@954
|
38 else
|
adamc@954
|
39 multiLiftExpInExp (n - 1) (E.liftExpInExp 0 e)
|
adamc@954
|
40
|
adamc@607
|
41 structure IS = IntBinarySet
|
adamc@607
|
42 structure IM = IntBinaryMap
|
adamc@607
|
43
|
adamc@607
|
44 structure SS = BinarySetFn(struct
|
adamc@607
|
45 type ord_key = string
|
adamc@607
|
46 val compare = String.compare
|
adamc@607
|
47 end)
|
adamc@607
|
48
|
adamc@607
|
49 type state = {
|
adamc@608
|
50 exported : IS.set,
|
adamc@954
|
51 export_decls : decl list,
|
adamc@954
|
52
|
adamc@954
|
53 cpsed : exp' IM.map,
|
adamc@954
|
54 rpc : IS.set
|
adamc@607
|
55 }
|
adamc@607
|
56
|
adamc@607
|
57 fun frob file =
|
adamc@607
|
58 let
|
adamc@908
|
59 val rpcBaseIds = foldl (fn ((d, _), rpcIds) =>
|
adamc@908
|
60 case d of
|
adamc@908
|
61 DVal (_, n, _, (EFfi ("Basis", "rpc"), _), _) => IS.add (rpcIds, n)
|
adamc@908
|
62 | DVal (_, n, _, (ENamed n', _), _) => if IS.member (rpcIds, n') then
|
adamc@908
|
63 IS.add (rpcIds, n)
|
adamc@908
|
64 else
|
adamc@908
|
65 rpcIds
|
adamc@908
|
66 | _ => rpcIds)
|
adamc@908
|
67 IS.empty file
|
adamc@607
|
68
|
adamc@609
|
69 val tfuncs = foldl
|
adamc@609
|
70 (fn ((d, _), tfuncs) =>
|
adamc@609
|
71 let
|
adamc@642
|
72 fun doOne ((x, n, t, e, _), tfuncs) =
|
adamc@609
|
73 let
|
adamc@642
|
74 val loc = #2 e
|
adamc@642
|
75
|
adamc@642
|
76 fun crawl (t, e, args) =
|
adamc@642
|
77 case (#1 t, #1 e) of
|
adamc@642
|
78 (CApp (_, ran), _) =>
|
adamc@642
|
79 SOME (x, rev args, ran, e)
|
adamc@642
|
80 | (TFun (arg, rest), EAbs (x, _, _, e)) =>
|
adamc@642
|
81 crawl (rest, e, (x, arg) :: args)
|
adamc@642
|
82 | (TFun (arg, rest), _) =>
|
adamc@642
|
83 crawl (rest, (EApp (e, (ERel (length args), loc)), loc), ("x", arg) :: args)
|
adamc@609
|
84 | _ => NONE
|
adamc@609
|
85 in
|
adamc@642
|
86 case crawl (t, e, []) of
|
adamc@609
|
87 NONE => tfuncs
|
adamc@609
|
88 | SOME sg => IM.insert (tfuncs, n, sg)
|
adamc@609
|
89 end
|
adamc@609
|
90 in
|
adamc@609
|
91 case d of
|
adamc@609
|
92 DVal vi => doOne (vi, tfuncs)
|
adamc@609
|
93 | DValRec vis => foldl doOne tfuncs vis
|
adamc@609
|
94 | _ => tfuncs
|
adamc@609
|
95 end)
|
adamc@609
|
96 IM.empty file
|
adamc@609
|
97
|
adamc@607
|
98 fun exp (e, st) =
|
adamc@649
|
99 let
|
adamc@649
|
100 fun getApp (e', args) =
|
adamc@908
|
101 case e' of
|
adamc@908
|
102 ENamed n => SOME (n, args)
|
adamc@908
|
103 | EApp (e1, e2) => getApp (#1 e1, e2 :: args)
|
adamc@908
|
104 | _ => NONE
|
adamc@642
|
105
|
adamc@908
|
106 fun newRpc (trans : exp, st : state) =
|
adamc@908
|
107 case getApp (#1 trans, []) of
|
adamc@908
|
108 NONE => (ErrorMsg.errorAt (#2 trans)
|
adamc@908
|
109 "RPC code doesn't use a named function or transaction";
|
adamc@908
|
110 (#1 trans, st))
|
adamc@908
|
111 | SOME (n, args) =>
|
adamc@908
|
112 case IM.find (tfuncs, n) of
|
adamc@908
|
113 NONE => ((*Print.prefaces "BAD" [("e", CorePrint.p_exp CoreEnv.empty (e, loc))];*)
|
adamc@908
|
114 raise Fail ("Rpcify: Undetected transaction function " ^ Int.toString n))
|
adamc@908
|
115 | SOME (_, _, ran, _) =>
|
adamc@908
|
116 let
|
adamc@908
|
117 val loc = #2 trans
|
adamc@642
|
118
|
adamc@908
|
119 val (exported, export_decls) =
|
adamc@908
|
120 if IS.member (#exported st, n) then
|
adamc@908
|
121 (#exported st, #export_decls st)
|
adamc@908
|
122 else
|
adamc@908
|
123 (IS.add (#exported st, n),
|
adamc@908
|
124 (DExport (Rpc ReadWrite, n), loc) :: #export_decls st)
|
adamc@642
|
125
|
adamc@908
|
126 val st = {exported = exported,
|
adamc@954
|
127 export_decls = export_decls,
|
adamc@954
|
128 cpsed = #cpsed st,
|
adamc@954
|
129 rpc = #rpc st}
|
adamc@642
|
130
|
adamc@908
|
131 val k = (ECApp ((EFfi ("Basis", "return"), loc),
|
adamc@908
|
132 (CFfi ("Basis", "transaction"), loc)), loc)
|
adamc@908
|
133 val k = (ECApp (k, ran), loc)
|
adamc@908
|
134 val k = (EApp (k, (EFfi ("Basis", "transaction_monad"), loc)), loc)
|
adamc@908
|
135 val e' = EServerCall (n, args, k, ran, ran)
|
adamc@651
|
136 in
|
adamc@908
|
137 (e', st)
|
adamc@651
|
138 end
|
adamc@649
|
139 in
|
adamc@649
|
140 case e of
|
adamc@908
|
141 EApp ((ECApp ((EFfi ("Basis", "rpc"), _), ran), _), trans) => newRpc (trans, st)
|
adamc@908
|
142 | EApp ((ECApp ((ENamed n, _), ran), _), trans) =>
|
adamc@908
|
143 if IS.member (rpcBaseIds, n) then
|
adamc@908
|
144 newRpc (trans, st)
|
adamc@908
|
145 else
|
adamc@908
|
146 (e, st)
|
adamc@642
|
147
|
adamc@954
|
148 | ENamed n =>
|
adamc@954
|
149 (case IM.find (#cpsed st, n) of
|
adamc@954
|
150 NONE => (e, st)
|
adamc@954
|
151 | SOME re => (re, st))
|
adamc@954
|
152
|
adamc@649
|
153 | _ => (e, st)
|
adamc@649
|
154 end
|
adamc@607
|
155
|
adamc@642
|
156 and doExp (e, st) = U.Exp.foldMap {kind = fn x => x,
|
adamc@642
|
157 con = fn x => x,
|
adamc@642
|
158 exp = exp} st (ReduceLocal.reduceExp e)
|
adamc@642
|
159
|
adamc@607
|
160 fun decl (d, st : state) =
|
adamc@607
|
161 let
|
adamc@954
|
162 val makesServerCall = U.Exp.exists {kind = fn _ => false,
|
adamc@954
|
163 con = fn _ => false,
|
adamc@954
|
164 exp = fn EFfi ("Basis", "rpc") => true
|
adamc@954
|
165 | ENamed n => IS.member (#rpc st, n)
|
adamc@954
|
166 | _ => false}
|
adamc@954
|
167
|
adamc@954
|
168 val (d, st) =
|
adamc@954
|
169 case #1 d of
|
adamc@954
|
170 DValRec vis =>
|
adamc@954
|
171 if List.exists (fn (_, _, _, e, _) => makesServerCall e) vis then
|
adamc@954
|
172 let
|
adamc@956
|
173 val rpc = foldl (fn ((_, n, _, _, _), rpc) =>
|
adamc@956
|
174 IS.add (rpc, n)) (#rpc st) vis
|
adamc@954
|
175
|
adamc@956
|
176 val (cpsed, vis') =
|
adamc@956
|
177 foldl (fn (vi as (x, n, t, e, s), (cpsed, vis')) =>
|
adamc@956
|
178 let
|
adamc@956
|
179 fun getArgs (t, acc) =
|
adamc@956
|
180 case #1 t of
|
adamc@956
|
181 TFun (dom, ran) =>
|
adamc@956
|
182 getArgs (ran, dom :: acc)
|
adamc@956
|
183 | _ => (rev acc, t)
|
adamc@956
|
184 val (ts, ran) = getArgs (t, [])
|
adamc@956
|
185 val ran = case #1 ran of
|
adamc@956
|
186 CApp (_, ran) => ran
|
adamc@956
|
187 | _ => raise Fail "Rpcify: Tail function not transactional"
|
adamc@956
|
188 val len = length ts
|
adamc@954
|
189
|
adamc@956
|
190 val loc = #2 e
|
adamc@956
|
191 val args = ListUtil.mapi
|
adamc@956
|
192 (fn (i, _) =>
|
adamc@956
|
193 (ERel (len - i - 1), loc))
|
adamc@956
|
194 ts
|
adamc@956
|
195 val k = (EFfi ("Basis", "return"), loc)
|
adamc@956
|
196 val trans = (CFfi ("Basis", "transaction"), loc)
|
adamc@956
|
197 val k = (ECApp (k, trans), loc)
|
adamc@956
|
198 val k = (ECApp (k, ran), loc)
|
adamc@956
|
199 val k = (EApp (k, (EFfi ("Basis", "transaction_monad"),
|
adamc@956
|
200 loc)), loc)
|
adamc@956
|
201 val re = (ETailCall (n, args, k, ran, ran), loc)
|
adamc@956
|
202 val (re, _) = foldr (fn (dom, (re, ran)) =>
|
adamc@956
|
203 ((EAbs ("x", dom, ran, re),
|
adamc@956
|
204 loc),
|
adamc@956
|
205 (TFun (dom, ran), loc)))
|
adamc@956
|
206 (re, ran) ts
|
adamc@954
|
207
|
adamc@956
|
208 val be = multiLiftExpInExp (len + 1) e
|
adamc@956
|
209 val be = ListUtil.foldli
|
adamc@956
|
210 (fn (i, _, be) =>
|
adamc@956
|
211 (EApp (be, (ERel (len - i), loc)), loc))
|
adamc@956
|
212 be ts
|
adamc@956
|
213 val ne = (EFfi ("Basis", "bind"), loc)
|
adamc@956
|
214 val ne = (ECApp (ne, trans), loc)
|
adamc@956
|
215 val ne = (ECApp (ne, ran), loc)
|
adamc@956
|
216 val unit = (TRecord (CRecord ((KType, loc), []),
|
adamc@956
|
217 loc), loc)
|
adamc@956
|
218 val ne = (ECApp (ne, unit), loc)
|
adamc@956
|
219 val ne = (EApp (ne, (EFfi ("Basis", "transaction_monad"),
|
adamc@956
|
220 loc)), loc)
|
adamc@956
|
221 val ne = (EApp (ne, be), loc)
|
adamc@956
|
222 val ne = (EApp (ne, (ERel 0, loc)), loc)
|
adamc@956
|
223 val tunit = (CApp (trans, unit), loc)
|
adamc@956
|
224 val kt = (TFun (ran, tunit), loc)
|
adamc@956
|
225 val ne = (EAbs ("k", kt, tunit, ne), loc)
|
adamc@956
|
226 val (ne, res) = foldr (fn (dom, (ne, ran)) =>
|
adamc@956
|
227 ((EAbs ("x", dom, ran, ne), loc),
|
adamc@956
|
228 (TFun (dom, ran), loc)))
|
adamc@956
|
229 (ne, (TFun (kt, tunit), loc)) ts
|
adamc@956
|
230 in
|
adamc@956
|
231 (IM.insert (cpsed, n, #1 re),
|
adamc@956
|
232 (x, n, res, ne, s) :: vis')
|
adamc@956
|
233 end)
|
adamc@956
|
234 (#cpsed st, []) vis
|
adamc@954
|
235 in
|
adamc@956
|
236 ((DValRec (rev vis'), ErrorMsg.dummySpan),
|
adamc@956
|
237 {exported = #exported st,
|
adamc@956
|
238 export_decls = #export_decls st,
|
adamc@956
|
239 cpsed = cpsed,
|
adamc@956
|
240 rpc = rpc})
|
adamc@954
|
241 end
|
adamc@954
|
242 else
|
adamc@954
|
243 (d, st)
|
adamc@954
|
244 | DVal (x, n, t, e, s) =>
|
adamc@954
|
245 (d,
|
adamc@954
|
246 {exported = #exported st,
|
adamc@954
|
247 export_decls = #export_decls st,
|
adamc@954
|
248 cpsed = #cpsed st,
|
adamc@954
|
249 rpc = if makesServerCall e then
|
adamc@954
|
250 IS.add (#rpc st, n)
|
adamc@954
|
251 else
|
adamc@954
|
252 #rpc st})
|
adamc@954
|
253 | _ => (d, st)
|
adamc@954
|
254
|
adamc@607
|
255 val (d, st) = U.Decl.foldMap {kind = fn x => x,
|
adamc@607
|
256 con = fn x => x,
|
adamc@607
|
257 exp = exp,
|
adamc@607
|
258 decl = fn x => x}
|
adamc@607
|
259 st d
|
adamc@607
|
260 in
|
adamc@908
|
261 (#export_decls st @ [d],
|
adamc@908
|
262 {exported = #exported st,
|
adamc@954
|
263 export_decls = [],
|
adamc@954
|
264 cpsed = #cpsed st,
|
adamc@954
|
265 rpc = #rpc st})
|
adamc@607
|
266 end
|
adamc@607
|
267
|
adamc@607
|
268 val (file, _) = ListUtil.foldlMapConcat decl
|
adamc@908
|
269 {exported = IS.empty,
|
adamc@954
|
270 export_decls = [],
|
adamc@954
|
271 cpsed = IM.empty,
|
adamc@954
|
272 rpc = rpcBaseIds}
|
adamc@607
|
273 file
|
adamc@607
|
274 in
|
adamc@607
|
275 file
|
adamc@607
|
276 end
|
adamc@607
|
277
|
adamc@607
|
278 end
|