comparison src/iflow.sml @ 1254:935a981f4380

Merge
author Adam Chlipala <adamc@hcoop.net>
date Thu, 06 May 2010 13:57:01 -0400
parents 9d65866ab9ab
children 3d06e0f7a6f3
comparison
equal deleted inserted replaced
1198:b52929351402 1254:935a981f4380
1 (* Copyright (c) 2010, Adam Chlipala
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - The names of contributors may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *)
27
28 structure Iflow :> IFLOW = struct
29
30 open Mono
31
32 structure IS = IntBinarySet
33 structure IM = IntBinaryMap
34
35 structure SK = struct
36 type ord_key = string
37 val compare = String.compare
38 end
39
40 structure SS = BinarySetFn(SK)
41 structure SM = BinaryMapFn(SK)
42
43 val writers = ["htmlifyInt_w",
44 "htmlifyFloat_w",
45 "htmlifyString_w",
46 "htmlifyBool_w",
47 "htmlifyTime_w",
48 "attrifyInt_w",
49 "attrifyFloat_w",
50 "attrifyString_w",
51 "attrifyChar_w",
52 "urlifyInt_w",
53 "urlifyFloat_w",
54 "urlifyString_w",
55 "urlifyBool_w",
56 "set_cookie"]
57
58 val writers = SS.addList (SS.empty, writers)
59
60 type lvar = int
61
62 datatype func =
63 DtCon0 of string
64 | DtCon1 of string
65 | UnCon of string
66 | Other of string
67
68 datatype exp =
69 Const of Prim.t
70 | Var of int
71 | Lvar of lvar
72 | Func of func * exp list
73 | Recd of (string * exp) list
74 | Proj of exp * string
75
76 datatype reln =
77 Known
78 | Sql of string
79 | PCon0 of string
80 | PCon1 of string
81 | Eq
82 | Ne
83 | Lt
84 | Le
85 | Gt
86 | Ge
87
88 datatype prop =
89 True
90 | False
91 | Unknown
92 | And of prop * prop
93 | Or of prop * prop
94 | Reln of reln * exp list
95 | Cond of exp * prop
96
97 local
98 open Print
99 val string = PD.string
100 in
101
102 fun p_func f =
103 string (case f of
104 DtCon0 s => s
105 | DtCon1 s => s
106 | UnCon s => "un" ^ s
107 | Other s => s)
108
109 fun p_exp e =
110 case e of
111 Const p => Prim.p_t p
112 | Var n => string ("x" ^ Int.toString n)
113 | Lvar n => string ("X" ^ Int.toString n)
114 | Func (f, es) => box [p_func f,
115 string "(",
116 p_list p_exp es,
117 string ")"]
118 | Recd xes => box [string "{",
119 p_list (fn (x, e) => box [string x,
120 space,
121 string "=",
122 space,
123 p_exp e]) xes,
124 string "}"]
125 | Proj (e, x) => box [p_exp e,
126 string ("." ^ x)]
127
128 fun p_bop s es =
129 case es of
130 [e1, e2] => box [p_exp e1,
131 space,
132 string s,
133 space,
134 p_exp e2]
135 | _ => raise Fail "Iflow.p_bop"
136
137 fun p_reln r es =
138 case r of
139 Known =>
140 (case es of
141 [e] => box [string "known(",
142 p_exp e,
143 string ")"]
144 | _ => raise Fail "Iflow.p_reln: Known")
145 | Sql s => box [string (s ^ "("),
146 p_list p_exp es,
147 string ")"]
148 | PCon0 s => box [string (s ^ "("),
149 p_list p_exp es,
150 string ")"]
151 | PCon1 s => box [string (s ^ "("),
152 p_list p_exp es,
153 string ")"]
154 | Eq => p_bop "=" es
155 | Ne => p_bop "<>" es
156 | Lt => p_bop "<" es
157 | Le => p_bop "<=" es
158 | Gt => p_bop ">" es
159 | Ge => p_bop ">=" es
160
161 fun p_prop p =
162 case p of
163 True => string "True"
164 | False => string "False"
165 | Unknown => string "??"
166 | And (p1, p2) => box [string "(",
167 p_prop p1,
168 string ")",
169 space,
170 string "&&",
171 space,
172 string "(",
173 p_prop p2,
174 string ")"]
175 | Or (p1, p2) => box [string "(",
176 p_prop p1,
177 string ")",
178 space,
179 string "||",
180 space,
181 string "(",
182 p_prop p2,
183 string ")"]
184 | Reln (r, es) => p_reln r es
185 | Cond (e, p) => box [string "(",
186 p_exp e,
187 space,
188 string "==",
189 space,
190 p_prop p,
191 string ")"]
192
193 end
194
195 fun isKnown e =
196 case e of
197 Const _ => true
198 | Func (_, es) => List.all isKnown es
199 | Recd xes => List.all (isKnown o #2) xes
200 | Proj (e, _) => isKnown e
201 | _ => false
202
203 fun simplify unif =
204 let
205 fun simplify e =
206 case e of
207 Const _ => e
208 | Var _ => e
209 | Lvar n =>
210 (case IM.find (unif, n) of
211 NONE => e
212 | SOME e => simplify e)
213 | Func (f, es) => Func (f, map simplify es)
214 | Recd xes => Recd (map (fn (x, e) => (x, simplify e)) xes)
215 | Proj (e, s) => Proj (simplify e, s)
216 in
217 simplify
218 end
219
220 datatype atom =
221 AReln of reln * exp list
222 | ACond of exp * prop
223
224 fun p_atom a =
225 p_prop (case a of
226 AReln x => Reln x
227 | ACond x => Cond x)
228
229 val debug = ref false
230
231 (* Congruence closure *)
232 structure Cc :> sig
233 type database
234
235 exception Contradiction
236
237 val database : unit -> database
238 val clear : database -> unit
239
240 val assert : database * atom -> unit
241 val check : database * atom -> bool
242
243 val p_database : database Print.printer
244
245 val builtFrom : database * {Base : exp list, Derived : exp} -> bool
246
247 val p_repOf : database -> exp Print.printer
248 end = struct
249
250 local
251 val count = ref 0
252 in
253 fun nodeId () =
254 let
255 val n = !count
256 in
257 count := n + 1;
258 n
259 end
260 end
261
262 exception Contradiction
263 exception Undetermined
264
265 structure CM = BinaryMapFn(struct
266 type ord_key = Prim.t
267 val compare = Prim.compare
268 end)
269
270 datatype node = Node of {Id : int,
271 Rep : node ref option ref,
272 Cons : node ref SM.map ref,
273 Variety : variety,
274 Known : bool ref,
275 Ge : Int64.int option ref}
276
277 and variety =
278 Dt0 of string
279 | Dt1 of string * node ref
280 | Prim of Prim.t
281 | Recrd of node ref SM.map ref * bool
282 | Nothing
283
284 type representative = node ref
285
286 type database = {Vars : representative IM.map ref,
287 Consts : representative CM.map ref,
288 Con0s : representative SM.map ref,
289 Records : (representative SM.map * representative) list ref,
290 Funcs : ((string * representative list) * representative) list ref}
291
292 fun database () = {Vars = ref IM.empty,
293 Consts = ref CM.empty,
294 Con0s = ref SM.empty,
295 Records = ref [],
296 Funcs = ref []}
297
298 fun clear (t : database) = (#Vars t := IM.empty;
299 #Consts t := CM.empty;
300 #Con0s t := SM.empty;
301 #Records t := [];
302 #Funcs t := [])
303
304 fun unNode n =
305 case !n of
306 Node r => r
307
308 open Print
309 val string = PD.string
310 val newline = PD.newline
311
312 fun p_rep n =
313 case !(#Rep (unNode n)) of
314 SOME n => p_rep n
315 | NONE =>
316 box [string (Int.toString (#Id (unNode n)) ^ ":"),
317 space,
318 case #Variety (unNode n) of
319 Nothing => string "?"
320 | Dt0 s => string ("Dt0(" ^ s ^ ")")
321 | Dt1 (s, n) => box[string ("Dt1(" ^ s ^ ","),
322 space,
323 p_rep n,
324 string ")"]
325 | Prim p => Prim.p_t p
326 | Recrd (ref m, b) => box [string "{",
327 p_list (fn (x, n) => box [string x,
328 space,
329 string "=",
330 space,
331 p_rep n]) (SM.listItemsi m),
332 string "}",
333 if b then
334 box [space,
335 string "(complete)"]
336 else
337 box []],
338 if !(#Known (unNode n)) then
339 string " (known)"
340 else
341 box [],
342 case !(#Ge (unNode n)) of
343 NONE => box []
344 | SOME n => string (" (>= " ^ Int64.toString n ^ ")")]
345
346 fun p_database (db : database) =
347 box [string "Vars:",
348 newline,
349 p_list_sep newline (fn (i, n) => box [string ("x" ^ Int.toString i),
350 space,
351 string "=",
352 space,
353 p_rep n]) (IM.listItemsi (!(#Vars db)))]
354
355 fun repOf (n : representative) : representative =
356 case !(#Rep (unNode n)) of
357 NONE => n
358 | SOME r =>
359 let
360 val r = repOf r
361 in
362 #Rep (unNode n) := SOME r;
363 r
364 end
365
366 fun markKnown r =
367 let
368 val r = repOf r
369 in
370 (*Print.preface ("markKnown", p_rep r);*)
371 if !(#Known (unNode r)) then
372 ()(*TextIO.print "Already known\n"*)
373 else
374 (#Known (unNode r) := true;
375 SM.app markKnown (!(#Cons (unNode r)));
376 case #Variety (unNode r) of
377 Dt1 (_, r) => markKnown r
378 | Recrd (xes, _) => SM.app markKnown (!xes)
379 | _ => ())
380 end
381
382 fun representative (db : database, e) =
383 let
384 fun rep e =
385 case e of
386 Const p => (case CM.find (!(#Consts db), p) of
387 SOME r => repOf r
388 | NONE =>
389 let
390 val r = ref (Node {Id = nodeId (),
391 Rep = ref NONE,
392 Cons = ref SM.empty,
393 Variety = Prim p,
394 Known = ref true,
395 Ge = ref (case p of
396 Prim.Int n => SOME n
397 | _ => NONE)})
398 in
399 #Consts db := CM.insert (!(#Consts db), p, r);
400 r
401 end)
402 | Var n => (case IM.find (!(#Vars db), n) of
403 SOME r => repOf r
404 | NONE =>
405 let
406 val r = ref (Node {Id = nodeId (),
407 Rep = ref NONE,
408 Cons = ref SM.empty,
409 Variety = Nothing,
410 Known = ref false,
411 Ge = ref NONE})
412 in
413 #Vars db := IM.insert (!(#Vars db), n, r);
414 r
415 end)
416 | Lvar _ => raise Undetermined
417 | Func (DtCon0 f, []) => (case SM.find (!(#Con0s db), f) of
418 SOME r => repOf r
419 | NONE =>
420 let
421 val r = ref (Node {Id = nodeId (),
422 Rep = ref NONE,
423 Cons = ref SM.empty,
424 Variety = Dt0 f,
425 Known = ref true,
426 Ge = ref NONE})
427 in
428 #Con0s db := SM.insert (!(#Con0s db), f, r);
429 r
430 end)
431 | Func (DtCon0 _, _) => raise Fail "Iflow.rep: DtCon0"
432 | Func (DtCon1 f, [e]) =>
433 let
434 val r = rep e
435 in
436 case SM.find (!(#Cons (unNode r)), f) of
437 SOME r => repOf r
438 | NONE =>
439 let
440 val r' = ref (Node {Id = nodeId (),
441 Rep = ref NONE,
442 Cons = ref SM.empty,
443 Variety = Dt1 (f, r),
444 Known = ref (!(#Known (unNode r))),
445 Ge = ref NONE})
446 in
447 #Cons (unNode r) := SM.insert (!(#Cons (unNode r)), f, r');
448 r'
449 end
450 end
451 | Func (DtCon1 _, _) => raise Fail "Iflow.rep: DtCon1"
452 | Func (UnCon f, [e]) =>
453 let
454 val r = rep e
455 in
456 case #Variety (unNode r) of
457 Dt1 (f', n) => if f' = f then
458 repOf n
459 else
460 raise Contradiction
461 | Nothing =>
462 let
463 val cons = ref SM.empty
464 val r' = ref (Node {Id = nodeId (),
465 Rep = ref NONE,
466 Cons = cons,
467 Variety = Nothing,
468 Known = ref (!(#Known (unNode r))),
469 Ge = ref NONE})
470
471 val r'' = ref (Node {Id = nodeId (),
472 Rep = ref NONE,
473 Cons = #Cons (unNode r),
474 Variety = Dt1 (f, r'),
475 Known = #Known (unNode r),
476 Ge = ref NONE})
477 in
478 cons := SM.insert (!cons, f, r'');
479 #Rep (unNode r) := SOME r'';
480 r'
481 end
482 | _ => raise Contradiction
483 end
484 | Func (UnCon _, _) => raise Fail "Iflow.rep: UnCon"
485 | Func (Other f, es) =>
486 let
487 val rs = map rep es
488 in
489 case List.find (fn (x : string * representative list, _) => x = (f, rs)) (!(#Funcs db)) of
490 NONE =>
491 let
492 val r = ref (Node {Id = nodeId (),
493 Rep = ref NONE,
494 Cons = ref SM.empty,
495 Variety = Nothing,
496 Known = ref (f = "allow"),
497 Ge = ref NONE})
498 in
499 #Funcs db := ((f, rs), r) :: (!(#Funcs db));
500 r
501 end
502 | SOME (_, r) => repOf r
503 end
504 | Recd xes =>
505 let
506 val xes = map (fn (x, e) => (x, rep e)) xes
507 val len = length xes
508 in
509 case List.find (fn (xes', _) =>
510 SM.numItems xes' = len
511 andalso List.all (fn (x, n) =>
512 case SM.find (xes', x) of
513 NONE => false
514 | SOME n' => n = repOf n') xes)
515 (!(#Records db)) of
516 SOME (_, r) => repOf r
517 | NONE =>
518 let
519 val xes = foldl SM.insert' SM.empty xes
520
521 val r' = ref (Node {Id = nodeId (),
522 Rep = ref NONE,
523 Cons = ref SM.empty,
524 Variety = Recrd (ref xes, true),
525 Known = ref false,
526 Ge = ref NONE})
527 in
528 #Records db := (xes, r') :: (!(#Records db));
529 r'
530 end
531 end
532 | Proj (e, f) =>
533 let
534 val r = rep e
535 in
536 case #Variety (unNode r) of
537 Recrd (xes, _) =>
538 (case SM.find (!xes, f) of
539 SOME r => repOf r
540 | NONE => let
541 val r = ref (Node {Id = nodeId (),
542 Rep = ref NONE,
543 Cons = ref SM.empty,
544 Variety = Nothing,
545 Known = ref (!(#Known (unNode r))),
546 Ge = ref NONE})
547 in
548 xes := SM.insert (!xes, f, r);
549 r
550 end)
551 | Nothing =>
552 let
553 val r' = ref (Node {Id = nodeId (),
554 Rep = ref NONE,
555 Cons = ref SM.empty,
556 Variety = Nothing,
557 Known = ref (!(#Known (unNode r))),
558 Ge = ref NONE})
559
560 val r'' = ref (Node {Id = nodeId (),
561 Rep = ref NONE,
562 Cons = #Cons (unNode r),
563 Variety = Recrd (ref (SM.insert (SM.empty, f, r')), false),
564 Known = #Known (unNode r),
565 Ge = ref NONE})
566 in
567 #Rep (unNode r) := SOME r'';
568 r'
569 end
570 | _ => raise Contradiction
571 end
572 in
573 rep e
574 end
575
576 fun p_repOf db e = p_rep (representative (db, e))
577
578 fun assert (db, a) =
579 let
580 fun markEq (r1, r2) =
581 let
582 val r1 = repOf r1
583 val r2 = repOf r2
584 in
585 if r1 = r2 then
586 ()
587 else case (#Variety (unNode r1), #Variety (unNode r2)) of
588 (Prim p1, Prim p2) => if Prim.equal (p1, p2) then
589 ()
590 else
591 raise Contradiction
592 | (Dt0 f1, Dt0 f2) => if f1 = f2 then
593 ()
594 else
595 raise Contradiction
596 | (Dt1 (f1, r1), Dt1 (f2, r2)) => if f1 = f2 then
597 markEq (r1, r2)
598 else
599 raise Contradiction
600 | (Recrd (xes1, _), Recrd (xes2, _)) =>
601 let
602 fun unif (xes1, xes2) =
603 SM.appi (fn (x, r1) =>
604 case SM.find (!xes2, x) of
605 NONE => xes2 := SM.insert (!xes2, x, r1)
606 | SOME r2 => markEq (r1, r2)) (!xes1)
607 in
608 unif (xes1, xes2);
609 unif (xes2, xes1)
610 end
611 | (Nothing, _) => mergeNodes (r1, r2)
612 | (_, Nothing) => mergeNodes (r2, r1)
613 | _ => raise Contradiction
614 end
615
616 and mergeNodes (r1, r2) =
617 (#Rep (unNode r1) := SOME r2;
618 if !(#Known (unNode r1)) then
619 markKnown r2
620 else
621 ();
622 if !(#Known (unNode r2)) then
623 markKnown r1
624 else
625 ();
626 #Cons (unNode r2) := SM.unionWith #1 (!(#Cons (unNode r2)), !(#Cons (unNode r1)));
627
628 case !(#Ge (unNode r1)) of
629 NONE => ()
630 | SOME n1 =>
631 case !(#Ge (unNode r2)) of
632 NONE => #Ge (unNode r2) := SOME n1
633 | SOME n2 => #Ge (unNode r2) := SOME (Int64.max (n1, n2));
634
635 compactFuncs ())
636
637 and compactFuncs () =
638 let
639 fun loop funcs =
640 case funcs of
641 [] => []
642 | (fr as ((f, rs), r)) :: rest =>
643 let
644 val rest = List.filter (fn ((f' : string, rs'), r') =>
645 if f' = f
646 andalso ListPair.allEq (fn (r1, r2) =>
647 repOf r1 = repOf r2)
648 (rs, rs') then
649 (markEq (r, r');
650 false)
651 else
652 true) rest
653 in
654 fr :: loop rest
655 end
656 in
657 #Funcs db := loop (!(#Funcs db))
658 end
659 in
660 case a of
661 ACond _ => ()
662 | AReln x =>
663 case x of
664 (Known, [e]) =>
665 ((*Print.prefaces "Before" [("e", p_exp e),
666 ("db", p_database db)];*)
667 markKnown (representative (db, e))(*;
668 Print.prefaces "After" [("e", p_exp e),
669 ("db", p_database db)]*))
670 | (PCon0 f, [e]) =>
671 let
672 val r = representative (db, e)
673 in
674 case #Variety (unNode r) of
675 Dt0 f' => if f = f' then
676 ()
677 else
678 raise Contradiction
679 | Nothing =>
680 (case SM.find (!(#Con0s db), f) of
681 SOME r' => markEq (r, r')
682 | NONE =>
683 let
684 val r' = ref (Node {Id = nodeId (),
685 Rep = ref NONE,
686 Cons = ref SM.empty,
687 Variety = Dt0 f,
688 Known = ref false,
689 Ge = ref NONE})
690 in
691 #Rep (unNode r) := SOME r';
692 #Con0s db := SM.insert (!(#Con0s db), f, r')
693 end)
694 | _ => raise Contradiction
695 end
696 | (PCon1 f, [e]) =>
697 let
698 val r = representative (db, e)
699 in
700 case #Variety (unNode r) of
701 Dt1 (f', e') => if f = f' then
702 ()
703 else
704 raise Contradiction
705 | Nothing =>
706 let
707 val cons = ref SM.empty
708
709 val r'' = ref (Node {Id = nodeId (),
710 Rep = ref NONE,
711 Cons = cons,
712 Variety = Nothing,
713 Known = ref (!(#Known (unNode r))),
714 Ge = ref NONE})
715
716 val r' = ref (Node {Id = nodeId (),
717 Rep = ref NONE,
718 Cons = ref SM.empty,
719 Variety = Dt1 (f, r''),
720 Known = #Known (unNode r),
721 Ge = ref NONE})
722 in
723 cons := SM.insert (!cons, f, r');
724 #Rep (unNode r) := SOME r'
725 end
726 | _ => raise Contradiction
727 end
728 | (Eq, [e1, e2]) =>
729 markEq (representative (db, e1), representative (db, e2))
730 | (Ge, [e1, e2]) =>
731 let
732 val r1 = representative (db, e1)
733 val r2 = representative (db, e2)
734 in
735 case !(#Ge (unNode (repOf r2))) of
736 NONE => ()
737 | SOME n2 =>
738 case !(#Ge (unNode (repOf r1))) of
739 NONE => #Ge (unNode (repOf r1)) := SOME n2
740 | SOME n1 => #Ge (unNode (repOf r1)) := SOME (Int64.max (n1, n2))
741 end
742 | _ => ()
743 end handle Undetermined => ()
744
745 fun check (db, a) =
746 (case a of
747 ACond _ => false
748 | AReln x =>
749 case x of
750 (Known, [e]) =>
751 let
752 fun isKnown r =
753 let
754 val r = repOf r
755 in
756 !(#Known (unNode r))
757 orelse case #Variety (unNode r) of
758 Dt1 (_, r) => isKnown r
759 | Recrd (xes, true) => List.all isKnown (SM.listItems (!xes))
760 | _ => false
761 end
762
763 val r = representative (db, e)
764 in
765 isKnown r
766 end
767 | (PCon0 f, [e]) =>
768 (case #Variety (unNode (representative (db, e))) of
769 Dt0 f' => f' = f
770 | _ => false)
771 | (PCon1 f, [e]) =>
772 (case #Variety (unNode (representative (db, e))) of
773 Dt1 (f', _) => f' = f
774 | _ => false)
775 | (Eq, [e1, e2]) =>
776 let
777 val r1 = representative (db, e1)
778 val r2 = representative (db, e2)
779 in
780 repOf r1 = repOf r2
781 end
782 | (Ge, [e1, e2]) =>
783 let
784 val r1 = representative (db, e1)
785 val r2 = representative (db, e2)
786 in
787 case (!(#Ge (unNode (repOf r1))), #Variety (unNode (repOf r2))) of
788 (SOME n1, Prim (Prim.Int n2)) => Int64.>= (n1, n2)
789 | _ => false
790 end
791 | _ => false)
792 handle Undetermined => false
793
794 fun builtFrom (db, {Base = bs, Derived = d}) =
795 let
796 val bs = map (fn b => representative (db, b)) bs
797
798 fun loop d =
799 let
800 val d = repOf d
801 in
802 !(#Known (unNode d))
803 orelse List.exists (fn b => repOf b = d) bs
804 orelse (case #Variety (unNode d) of
805 Dt0 _ => true
806 | Dt1 (_, d) => loop d
807 | Prim _ => true
808 | Recrd (xes, _) => List.all loop (SM.listItems (!xes))
809 | Nothing => false)
810 orelse List.exists (fn r => List.exists (fn b => repOf b = repOf r) bs)
811 (SM.listItems (!(#Cons (unNode d))))
812 end
813
814 fun decomp e =
815 case e of
816 Func (Other _, es) => List.all decomp es
817 | _ => loop (representative (db, e))
818 in
819 decomp d
820 end handle Undetermined => false
821
822 end
823
824 val tabs = ref (SM.empty : (string list * string list list) SM.map)
825
826 fun patCon pc =
827 case pc of
828 PConVar n => "C" ^ Int.toString n
829 | PConFfi {mod = m, datatyp = d, con = c, ...} => m ^ "." ^ d ^ "." ^ c
830
831 datatype chunk =
832 String of string
833 | Exp of Mono.exp
834
835 fun chunkify e =
836 case #1 e of
837 EPrim (Prim.String s) => [String s]
838 | EStrcat (e1, e2) =>
839 let
840 val chs1 = chunkify e1
841 val chs2 = chunkify e2
842 in
843 case chs2 of
844 String s2 :: chs2' =>
845 (case List.last chs1 of
846 String s1 => List.take (chs1, length chs1 - 1) @ String (s1 ^ s2) :: chs2'
847 | _ => chs1 @ chs2)
848 | _ => chs1 @ chs2
849 end
850 | _ => [Exp e]
851
852 type 'a parser = chunk list -> ('a * chunk list) option
853
854 fun always v chs = SOME (v, chs)
855
856 fun parse p s =
857 case p (chunkify s) of
858 SOME (v, []) => SOME v
859 | _ => NONE
860
861 fun const s chs =
862 case chs of
863 String s' :: chs => if String.isPrefix s s' then
864 SOME ((), if size s = size s' then
865 chs
866 else
867 String (String.extract (s', size s, NONE)) :: chs)
868 else
869 NONE
870 | _ => NONE
871
872 fun follow p1 p2 chs =
873 case p1 chs of
874 NONE => NONE
875 | SOME (v1, chs) =>
876 case p2 chs of
877 NONE => NONE
878 | SOME (v2, chs) => SOME ((v1, v2), chs)
879
880 fun wrap p f chs =
881 case p chs of
882 NONE => NONE
883 | SOME (v, chs) => SOME (f v, chs)
884
885 fun wrapP p f chs =
886 case p chs of
887 NONE => NONE
888 | SOME (v, chs) =>
889 case f v of
890 NONE => NONE
891 | SOME r => SOME (r, chs)
892
893 fun alt p1 p2 chs =
894 case p1 chs of
895 NONE => p2 chs
896 | v => v
897
898 fun altL ps =
899 case rev ps of
900 [] => (fn _ => NONE)
901 | p :: ps =>
902 foldl (fn (p1, p2) => alt p1 p2) p ps
903
904 fun opt p chs =
905 case p chs of
906 NONE => SOME (NONE, chs)
907 | SOME (v, chs) => SOME (SOME v, chs)
908
909 fun skip cp chs =
910 case chs of
911 String "" :: chs => skip cp chs
912 | String s :: chs' => if cp (String.sub (s, 0)) then
913 skip cp (String (String.extract (s, 1, NONE)) :: chs')
914 else
915 SOME ((), chs)
916 | _ => SOME ((), chs)
917
918 fun keep cp chs =
919 case chs of
920 String "" :: chs => keep cp chs
921 | String s :: chs' =>
922 let
923 val (befor, after) = Substring.splitl cp (Substring.full s)
924 in
925 if Substring.isEmpty befor then
926 NONE
927 else
928 SOME (Substring.string befor,
929 if Substring.isEmpty after then
930 chs'
931 else
932 String (Substring.string after) :: chs')
933 end
934 | _ => NONE
935
936 fun ws p = wrap (follow (skip (fn ch => ch = #" "))
937 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2)
938
939 fun log name p chs =
940 (if !debug then
941 (print (name ^ ": ");
942 app (fn String s => print s
943 | _ => print "???") chs;
944 print "\n")
945 else
946 ();
947 p chs)
948
949 fun list p chs =
950 altL [wrap (follow p (follow (ws (const ",")) (list p)))
951 (fn (v, ((), ls)) => v :: ls),
952 wrap (ws p) (fn v => [v]),
953 always []] chs
954
955 val ident = keep (fn ch => Char.isAlphaNum ch orelse ch = #"_")
956
957 val t_ident = wrapP ident (fn s => if String.isPrefix "T_" s then
958 SOME (String.extract (s, 2, NONE))
959 else
960 NONE)
961 val uw_ident = wrapP ident (fn s => if String.isPrefix "uw_" s andalso size s >= 4 then
962 SOME (str (Char.toUpper (String.sub (s, 3)))
963 ^ String.extract (s, 4, NONE))
964 else
965 NONE)
966
967 val field = wrap (follow t_ident
968 (follow (const ".")
969 uw_ident))
970 (fn (t, ((), f)) => (t, f))
971
972 datatype Rel =
973 Exps of exp * exp -> prop
974 | Props of prop * prop -> prop
975
976 datatype sqexp =
977 SqConst of Prim.t
978 | SqTrue
979 | SqFalse
980 | SqNot of sqexp
981 | Field of string * string
982 | Computed of string
983 | Binop of Rel * sqexp * sqexp
984 | SqKnown of sqexp
985 | Inj of Mono.exp
986 | SqFunc of string * sqexp
987 | Unmodeled
988 | Null
989
990 fun cmp s r = wrap (const s) (fn () => Exps (fn (e1, e2) => Reln (r, [e1, e2])))
991
992 val sqbrel = altL [cmp "=" Eq,
993 cmp "<>" Ne,
994 cmp "<=" Le,
995 cmp "<" Lt,
996 cmp ">=" Ge,
997 cmp ">" Gt,
998 wrap (const "AND") (fn () => Props And),
999 wrap (const "OR") (fn () => Props Or)]
1000
1001 datatype ('a, 'b) sum = inl of 'a | inr of 'b
1002
1003 fun string chs =
1004 case chs of
1005 String s :: chs =>
1006 if size s >= 2 andalso String.sub (s, 0) = #"'" then
1007 let
1008 fun loop (cs, acc) =
1009 case cs of
1010 [] => NONE
1011 | c :: cs =>
1012 if c = #"'" then
1013 SOME (String.implode (rev acc), cs)
1014 else if c = #"\\" then
1015 case cs of
1016 c :: cs => loop (cs, c :: acc)
1017 | _ => raise Fail "Iflow.string: Unmatched backslash escape"
1018 else
1019 loop (cs, c :: acc)
1020 in
1021 case loop (String.explode (String.extract (s, 1, NONE)), []) of
1022 NONE => NONE
1023 | SOME (s, []) => SOME (s, chs)
1024 | SOME (s, cs) => SOME (s, String (String.implode cs) :: chs)
1025 end
1026 else
1027 NONE
1028 | _ => NONE
1029
1030 val prim =
1031 altL [wrap (follow (wrapP (follow (keep Char.isDigit) (follow (const ".") (keep Char.isDigit)))
1032 (fn (x, ((), y)) => Option.map Prim.Float (Real64.fromString (x ^ "." ^ y))))
1033 (opt (const "::float8"))) #1,
1034 wrap (follow (wrapP (keep Char.isDigit)
1035 (Option.map Prim.Int o Int64.fromString))
1036 (opt (const "::int8"))) #1,
1037 wrap (follow (opt (const "E")) (follow string (opt (const "::text"))))
1038 (Prim.String o #1 o #2)]
1039
1040 fun known' chs =
1041 case chs of
1042 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs)
1043 | _ => NONE
1044
1045 fun sqlify chs =
1046 case chs of
1047 Exp (EFfiApp ("Basis", f, [e]), _) :: chs =>
1048 if String.isPrefix "sqlify" f then
1049 SOME (e, chs)
1050 else
1051 NONE
1052 | Exp (ECase (e, [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE), _),
1053 (EPrim (Prim.String "TRUE"), _)),
1054 ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE), _),
1055 (EPrim (Prim.String "FALSE"), _))], _), _) :: chs =>
1056 SOME (e, chs)
1057
1058 | _ => NONE
1059
1060 fun constK s = wrap (const s) (fn () => s)
1061
1062 val funcName = altL [constK "COUNT",
1063 constK "MIN",
1064 constK "MAX",
1065 constK "SUM",
1066 constK "AVG"]
1067
1068 val unmodeled = altL [const "COUNT(*)",
1069 const "CURRENT_TIMESTAMP"]
1070
1071 fun sqexp chs =
1072 log "sqexp"
1073 (altL [wrap prim SqConst,
1074 wrap (const "TRUE") (fn () => SqTrue),
1075 wrap (const "FALSE") (fn () => SqFalse),
1076 wrap (const "NULL") (fn () => Null),
1077 wrap field Field,
1078 wrap uw_ident Computed,
1079 wrap known SqKnown,
1080 wrap func SqFunc,
1081 wrap unmodeled (fn () => Unmodeled),
1082 wrap sqlify Inj,
1083 wrap (follow (const "COALESCE(") (follow sqexp (follow (const ",")
1084 (follow (keep (fn ch => ch <> #")")) (const ")")))))
1085 (fn ((), (e, _)) => e),
1086 wrap (follow (const "(NOT ") (follow sqexp (const ")")))
1087 (fn ((), (e, _)) => SqNot e),
1088 wrap (follow (ws (const "("))
1089 (follow (wrap
1090 (follow sqexp
1091 (alt
1092 (wrap
1093 (follow (ws sqbrel)
1094 (ws sqexp))
1095 inl)
1096 (always (inr ()))))
1097 (fn (e1, sm) =>
1098 case sm of
1099 inl (bo, e2) => Binop (bo, e1, e2)
1100 | inr () => e1))
1101 (const ")")))
1102 (fn ((), (e, ())) => e)])
1103 chs
1104
1105 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")"))))
1106 (fn ((), ((), (e, ()))) => e) chs
1107
1108 and func chs = wrap (follow funcName (follow (const "(") (follow sqexp (const ")"))))
1109 (fn (f, ((), (e, ()))) => (f, e)) chs
1110
1111 datatype sitem =
1112 SqField of string * string
1113 | SqExp of sqexp * string
1114
1115 val sitem = alt (wrap (follow sqexp (follow (const " AS ") uw_ident))
1116 (fn (e, ((), s)) => SqExp (e, s)))
1117 (wrap field SqField)
1118
1119 val select = log "select"
1120 (wrap (follow (const "SELECT ") (list sitem))
1121 (fn ((), ls) => ls))
1122
1123 val fitem = wrap (follow uw_ident
1124 (follow (const " AS ")
1125 t_ident))
1126 (fn (t, ((), f)) => (t, f))
1127
1128 val from = log "from"
1129 (wrap (follow (const "FROM ") (list fitem))
1130 (fn ((), ls) => ls))
1131
1132 val wher = wrap (follow (ws (const "WHERE ")) sqexp)
1133 (fn ((), ls) => ls)
1134
1135 type query1 = {Select : sitem list,
1136 From : (string * string) list,
1137 Where : sqexp option}
1138
1139 val query1 = log "query1"
1140 (wrap (follow (follow select from) (opt wher))
1141 (fn ((fs, ts), wher) => {Select = fs, From = ts, Where = wher}))
1142
1143 datatype query =
1144 Query1 of query1
1145 | Union of query * query
1146
1147 val orderby = log "orderby"
1148 (wrap (follow (ws (const "ORDER BY "))
1149 (follow (list sqexp)
1150 (opt (ws (const "DESC")))))
1151 ignore)
1152
1153 fun query chs = log "query"
1154 (wrap
1155 (follow
1156 (alt (wrap (follow (const "((")
1157 (follow query
1158 (follow (const ") UNION (")
1159 (follow query (const "))")))))
1160 (fn ((), (q1, ((), (q2, ())))) => Union (q1, q2)))
1161 (wrap query1 Query1))
1162 (opt orderby))
1163 #1)
1164 chs
1165
1166 datatype dml =
1167 Insert of string * (string * sqexp) list
1168 | Delete of string * sqexp
1169 | Update of string * (string * sqexp) list * sqexp
1170
1171 val insert = log "insert"
1172 (wrapP (follow (const "INSERT INTO ")
1173 (follow uw_ident
1174 (follow (const " (")
1175 (follow (list uw_ident)
1176 (follow (const ") VALUES (")
1177 (follow (list sqexp)
1178 (const ")")))))))
1179 (fn ((), (tab, ((), (fs, ((), (es, ())))))) =>
1180 (SOME (tab, ListPair.zipEq (fs, es)))
1181 handle ListPair.UnequalLengths => NONE))
1182
1183 val delete = log "delete"
1184 (wrap (follow (const "DELETE FROM ")
1185 (follow uw_ident
1186 (follow (const " AS T_T WHERE ")
1187 sqexp)))
1188 (fn ((), (tab, ((), es))) => (tab, es)))
1189
1190 val setting = log "setting"
1191 (wrap (follow uw_ident (follow (const " = ") sqexp))
1192 (fn (f, ((), e)) => (f, e)))
1193
1194 val update = log "update"
1195 (wrap (follow (const "UPDATE ")
1196 (follow uw_ident
1197 (follow (const " AS T_T SET ")
1198 (follow (list setting)
1199 (follow (ws (const "WHERE "))
1200 sqexp)))))
1201 (fn ((), (tab, ((), (fs, ((), e))))) =>
1202 (tab, fs, e)))
1203
1204 val dml = log "dml"
1205 (altL [wrap insert Insert,
1206 wrap delete Delete,
1207 wrap update Update])
1208
1209 type check = exp * ErrorMsg.span
1210
1211 structure St :> sig
1212 val reset : unit -> unit
1213
1214 type stashed
1215 val stash : unit -> stashed
1216 val reinstate : stashed -> unit
1217
1218 type stashedPath
1219 val stashPath : unit -> stashedPath
1220 val reinstatePath : stashedPath -> unit
1221
1222 val nextVar : unit -> int
1223
1224 val assert : atom list -> unit
1225
1226 val addPath : check -> unit
1227
1228 val allowSend : atom list * exp list -> unit
1229 val send : check -> unit
1230
1231 val allowInsert : atom list -> unit
1232 val insert : ErrorMsg.span -> unit
1233
1234 val allowDelete : atom list -> unit
1235 val delete : ErrorMsg.span -> unit
1236
1237 val allowUpdate : atom list -> unit
1238 val update : ErrorMsg.span -> unit
1239
1240 val havocReln : reln -> unit
1241 val havocCookie : string -> unit
1242
1243 val check : atom -> bool
1244
1245 val debug : unit -> unit
1246 end = struct
1247
1248 val hnames = ref 1
1249
1250 type hyps = int * atom list * bool ref
1251
1252 val db = Cc.database ()
1253 val path = ref ([] : ((int * atom list) * check) option ref list)
1254 val hyps = ref (0, [] : atom list, ref false)
1255 val nvar = ref 0
1256
1257 fun setHyps (n', hs) =
1258 let
1259 val (n, _, _) = !hyps
1260 in
1261 if n' = n then
1262 ()
1263 else
1264 (hyps := (n', hs, ref false);
1265 Cc.clear db;
1266 app (fn a => Cc.assert (db, a)) hs)
1267 end
1268
1269 fun useKeys () =
1270 let
1271 val changed = ref false
1272
1273 fun findKeys (hyps, acc) =
1274 case hyps of
1275 [] => rev acc
1276 | (a as AReln (Sql tab, [r1])) :: hyps =>
1277 (case SM.find (!tabs, tab) of
1278 NONE => findKeys (hyps, a :: acc)
1279 | SOME (_, []) => findKeys (hyps, a :: acc)
1280 | SOME (_, ks) =>
1281 let
1282 fun finder (hyps, acc) =
1283 case hyps of
1284 [] => rev acc
1285 | (a as AReln (Sql tab', [r2])) :: hyps =>
1286 if tab' = tab andalso
1287 List.exists (List.all (fn f =>
1288 let
1289 val r =
1290 Cc.check (db,
1291 AReln (Eq, [Proj (r1, f),
1292 Proj (r2, f)]))
1293 in
1294 (*Print.prefaces "Fs"
1295 [("tab",
1296 Print.PD.string tab),
1297 ("r1",
1298 p_exp (Proj (r1, f))),
1299 ("r2",
1300 p_exp (Proj (r2, f))),
1301 ("r",
1302 Print.PD.string
1303 (Bool.toString r))];*)
1304 r
1305 end)) ks then
1306 (changed := true;
1307 Cc.assert (db, AReln (Eq, [r1, r2]));
1308 finder (hyps, acc))
1309 else
1310 finder (hyps, a :: acc)
1311 | a :: hyps => finder (hyps, a :: acc)
1312
1313 val hyps = finder (hyps, [])
1314 in
1315 findKeys (hyps, a :: acc)
1316 end)
1317 | a :: hyps => findKeys (hyps, a :: acc)
1318
1319 fun loop hs =
1320 let
1321 val hs = findKeys (hs, [])
1322 in
1323 if !changed then
1324 (changed := false;
1325 loop hs)
1326 else
1327 ()
1328 end
1329
1330 val (_, hs, _) = !hyps
1331 in
1332 (*print "useKeys\n";*)
1333 loop hs
1334 end
1335
1336 fun complete () =
1337 let
1338 val (_, _, bf) = !hyps
1339 in
1340 if !bf then
1341 ()
1342 else
1343 (bf := true;
1344 useKeys ())
1345 end
1346
1347 type stashed = int * ((int * atom list) * check) option ref list * (int * atom list)
1348 fun stash () = (!nvar, !path, (#1 (!hyps), #2 (!hyps)))
1349 fun reinstate (nv, p, h) =
1350 (nvar := nv;
1351 path := p;
1352 setHyps h)
1353
1354 type stashedPath = ((int * atom list) * check) option ref list
1355 fun stashPath () = !path
1356 fun reinstatePath p = path := p
1357
1358 fun nextVar () =
1359 let
1360 val n = !nvar
1361 in
1362 nvar := n + 1;
1363 n
1364 end
1365
1366 fun assert ats =
1367 let
1368 val n = !hnames
1369 val (_, hs, _) = !hyps
1370 in
1371 hnames := n + 1;
1372 hyps := (n, ats @ hs, ref false);
1373 app (fn a => Cc.assert (db, a)) ats
1374 end
1375
1376 fun addPath c = path := ref (SOME ((#1 (!hyps), #2 (!hyps)), c)) :: !path
1377
1378 val sendable = ref ([] : (atom list * exp list) list)
1379
1380 fun checkGoals goals k =
1381 let
1382 fun checkGoals goals unifs =
1383 case goals of
1384 [] => k unifs
1385 | AReln (Sql tab, [Lvar lv]) :: goals =>
1386 let
1387 val saved = stash ()
1388 val (_, hyps, _) = !hyps
1389
1390 fun tryAll unifs hyps =
1391 case hyps of
1392 [] => false
1393 | AReln (Sql tab', [e]) :: hyps =>
1394 (tab' = tab andalso
1395 checkGoals goals (IM.insert (unifs, lv, e)))
1396 orelse tryAll unifs hyps
1397 | _ :: hyps => tryAll unifs hyps
1398 in
1399 tryAll unifs hyps
1400 end
1401 | (g as AReln (r, es)) :: goals =>
1402 (complete ();
1403 (if Cc.check (db, AReln (r, map (simplify unifs) es)) then
1404 true
1405 else
1406 ((*Print.preface ("Fail", p_atom (AReln (r, map (simplify unifs) es)));*)
1407 false))
1408 andalso checkGoals goals unifs)
1409 | ACond _ :: _ => false
1410 in
1411 checkGoals goals IM.empty
1412 end
1413
1414 fun buildable (e, loc) =
1415 let
1416 fun doPols pols acc =
1417 case pols of
1418 [] =>
1419 let
1420 val b = Cc.builtFrom (db, {Base = acc, Derived = e})
1421 in
1422 (*Print.prefaces "buildable" [("Base", Print.p_list p_exp acc),
1423 ("Derived", p_exp e),
1424 ("Hyps", Print.p_list p_atom (#2 (!hyps))),
1425 ("Good", Print.PD.string (Bool.toString b))];*)
1426 b
1427 end
1428 | (goals, es) :: pols =>
1429 checkGoals goals (fn unifs => doPols pols (map (simplify unifs) es @ acc))
1430 orelse doPols pols acc
1431 in
1432 if doPols (!sendable) [] then
1433 ()
1434 else
1435 let
1436 val (_, hs, _) = !hyps
1437 in
1438 ErrorMsg.errorAt loc "The information flow policy may be violated here.";
1439 Print.prefaces "Situation" [("User learns", p_exp e),
1440 ("Hypotheses", Print.p_list p_atom hs),
1441 ("E-graph", Cc.p_database db)]
1442 end
1443 end
1444
1445 fun checkPaths () =
1446 let
1447 val (n, hs, _) = !hyps
1448 val hs = (n, hs)
1449 in
1450 app (fn r =>
1451 case !r of
1452 NONE => ()
1453 | SOME (hs, e) =>
1454 (r := NONE;
1455 setHyps hs;
1456 buildable e)) (!path);
1457 setHyps hs
1458 end
1459
1460 fun allowSend v = ((*Print.prefaces "Allow" [("goals", Print.p_list p_atom (#1 v)),
1461 ("exps", Print.p_list p_exp (#2 v))];*)
1462 sendable := v :: !sendable)
1463
1464 fun send (e, loc) = ((*Print.preface ("Send[" ^ Bool.toString uk ^ "]", p_exp e);*)
1465 complete ();
1466 checkPaths ();
1467 if isKnown e then
1468 ()
1469 else
1470 buildable (e, loc))
1471
1472 fun doable pols (loc : ErrorMsg.span) =
1473 let
1474 val pols = !pols
1475 in
1476 complete ();
1477 if List.exists (fn goals =>
1478 if checkGoals goals (fn _ => true) then
1479 ((*Print.prefaces "Match" [("goals", Print.p_list p_atom goals),
1480 ("hyps", Print.p_list p_atom (#2 (!hyps)))];*)
1481 true)
1482 else
1483 ((*Print.prefaces "No match" [("goals", Print.p_list p_atom goals)(*,
1484 ("hyps", Print.p_list p_atom (#2 (!hyps)))*)];*)
1485 false)) pols then
1486 ()
1487 else
1488 let
1489 val (_, hs, _) = !hyps
1490 in
1491 ErrorMsg.errorAt loc "The database update policy may be violated here.";
1492 Print.prefaces "Situation" [("Hypotheses", Print.p_list p_atom hs)(*,
1493 ("E-graph", Cc.p_database db)*)]
1494 end
1495 end
1496
1497 val insertable = ref ([] : atom list list)
1498 fun allowInsert v = insertable := v :: !insertable
1499 val insert = doable insertable
1500
1501 val updatable = ref ([] : atom list list)
1502 fun allowUpdate v = updatable := v :: !updatable
1503 val update = doable updatable
1504
1505 val deletable = ref ([] : atom list list)
1506 fun allowDelete v = deletable := v :: !deletable
1507 val delete = doable deletable
1508
1509 fun reset () = (Cc.clear db;
1510 path := [];
1511 hyps := (0, [], ref false);
1512 nvar := 0;
1513 sendable := [];
1514 insertable := [];
1515 updatable := [];
1516 deletable := [])
1517
1518 fun havocReln r =
1519 let
1520 val n = !hnames
1521 val (_, hs, _) = !hyps
1522 in
1523 hnames := n + 1;
1524 hyps := (n, List.filter (fn AReln (r', _) => r' <> r | _ => true) hs, ref false)
1525 end
1526
1527 fun havocCookie cname =
1528 let
1529 val cname = "cookie/" ^ cname
1530 val n = !hnames
1531 val (_, hs, _) = !hyps
1532 in
1533 hnames := n + 1;
1534 hyps := (n, List.filter (fn AReln (Eq, [_, Func (Other f, [])]) => f <> cname | _ => true) hs, ref false)
1535 end
1536
1537 fun check a = Cc.check (db, a)
1538
1539 fun debug () =
1540 let
1541 val (_, hs, _) = !hyps
1542 in
1543 Print.preface ("Hyps", Print.p_list p_atom hs)
1544 end
1545
1546 end
1547
1548
1549 fun removeDups (ls : (string * string) list) =
1550 case ls of
1551 [] => []
1552 | x :: ls =>
1553 let
1554 val ls = removeDups ls
1555 in
1556 if List.exists (fn x' => x' = x) ls then
1557 ls
1558 else
1559 x :: ls
1560 end
1561
1562 fun deinj env e =
1563 case #1 e of
1564 ERel n => SOME (List.nth (env, n))
1565 | EField (e, f) =>
1566 (case deinj env e of
1567 NONE => NONE
1568 | SOME e => SOME (Proj (e, f)))
1569 | EApp ((EFfi mf, _), e) =>
1570 if Settings.isEffectful mf orelse Settings.isBenignEffectful mf then
1571 NONE
1572 else (case deinj env e of
1573 NONE => NONE
1574 | SOME e => SOME (Func (Other (#1 mf ^ "." ^ #2 mf), [e])))
1575 | _ => NONE
1576
1577 fun expIn rv env rvOf =
1578 let
1579 fun expIn e =
1580 let
1581 fun default () = inl (rv ())
1582 in
1583 case e of
1584 SqConst p => inl (Const p)
1585 | SqTrue => inl (Func (DtCon0 "Basis.bool.True", []))
1586 | SqFalse => inl (Func (DtCon0 "Basis.bool.False", []))
1587 | Null => inl (Func (DtCon0 "None", []))
1588 | SqNot e =>
1589 inr (case expIn e of
1590 inl e => Reln (Eq, [e, Func (DtCon0 "Basis.bool.False", [])])
1591 | inr _ => Unknown)
1592 | Field (v, f) => inl (Proj (rvOf v, f))
1593 | Computed _ => default ()
1594 | Binop (bo, e1, e2) =>
1595 let
1596 val e1 = expIn e1
1597 val e2 = expIn e2
1598 in
1599 inr (case (bo, e1, e2) of
1600 (Exps f, inl e1, inl e2) => f (e1, e2)
1601 | (Props f, v1, v2) =>
1602 let
1603 fun pin v =
1604 case v of
1605 inl e => Reln (Eq, [e, Func (DtCon0 "Basis.bool.True", [])])
1606 | inr p => p
1607 in
1608 f (pin v1, pin v2)
1609 end
1610 | _ => Unknown)
1611 end
1612 | SqKnown e =>
1613 (case expIn e of
1614 inl e => inr (Reln (Known, [e]))
1615 | _ => inr Unknown)
1616 | Inj e =>
1617 inl (case deinj env e of
1618 NONE => rv ()
1619 | SOME e => e)
1620 | SqFunc (f, e) =>
1621 (case expIn e of
1622 inl e => inl (Func (Other f, [e]))
1623 | _ => default ())
1624
1625 | Unmodeled => inl (Func (Other "allow", [rv ()]))
1626 end
1627 in
1628 expIn
1629 end
1630
1631 fun decomp {Save = save, Restore = restore, Add = add} =
1632 let
1633 fun go p k =
1634 case p of
1635 True => (k () handle Cc.Contradiction => ())
1636 | False => ()
1637 | Unknown => ()
1638 | And (p1, p2) => go p1 (fn () => go p2 k)
1639 | Or (p1, p2) =>
1640 let
1641 val saved = save ()
1642 in
1643 go p1 k;
1644 restore saved;
1645 go p2 k
1646 end
1647 | Reln x => (add (AReln x); k ())
1648 | Cond x => (add (ACond x); k ())
1649 in
1650 go
1651 end
1652
1653 datatype queryMode =
1654 SomeCol of {New : (string * exp) option, Old : (string * exp) option, Outs : exp list} -> unit
1655 | AllCols of exp -> unit
1656
1657 type 'a doQuery = {
1658 Env : exp list,
1659 NextVar : unit -> exp,
1660 Add : atom -> unit,
1661 Save : unit -> 'a,
1662 Restore : 'a -> unit,
1663 Cont : queryMode
1664 }
1665
1666 fun doQuery (arg : 'a doQuery) (e as (_, loc)) =
1667 let
1668 fun default () = ErrorMsg.errorAt loc "Information flow checker can't parse SQL query"
1669 in
1670 case parse query e of
1671 NONE => default ()
1672 | SOME q =>
1673 let
1674 fun doQuery q =
1675 case q of
1676 Query1 r =>
1677 let
1678 val new = ref NONE
1679 val old = ref NONE
1680
1681 val rvs = map (fn (tab, v) =>
1682 let
1683 val nv = #NextVar arg ()
1684 in
1685 case v of
1686 "New" => new := SOME (tab, nv)
1687 | "Old" => old := SOME (tab, nv)
1688 | _ => ();
1689 (v, nv)
1690 end) (#From r)
1691
1692 fun rvOf v =
1693 case List.find (fn (v', _) => v' = v) rvs of
1694 NONE => raise Fail "Iflow.queryProp: Bad table variable"
1695 | SOME (_, e) => e
1696
1697 val expIn = expIn (#NextVar arg) (#Env arg) rvOf
1698
1699 val saved = #Save arg ()
1700 fun addFrom () = app (fn (t, v) => #Add arg (AReln (Sql t, [rvOf v]))) (#From r)
1701
1702 fun usedFields e =
1703 case e of
1704 SqConst _ => []
1705 | SqTrue => []
1706 | SqFalse => []
1707 | Null => []
1708 | SqNot e => usedFields e
1709 | Field (v, f) => [(false, Proj (rvOf v, f))]
1710 | Computed _ => []
1711 | Binop (_, e1, e2) => usedFields e1 @ usedFields e2
1712 | SqKnown _ => []
1713 | Inj e =>
1714 (case deinj (#Env arg) e of
1715 NONE => (ErrorMsg.errorAt loc "Expression injected into SQL is too complicated";
1716 [])
1717 | SOME e => [(true, e)])
1718 | SqFunc (_, e) => usedFields e
1719 | Unmodeled => []
1720
1721 fun normal' () =
1722 case #Cont arg of
1723 SomeCol k =>
1724 let
1725 val sis = map (fn si =>
1726 case si of
1727 SqField (v, f) => Proj (rvOf v, f)
1728 | SqExp (e, f) =>
1729 case expIn e of
1730 inr _ => #NextVar arg ()
1731 | inl e => e) (#Select r)
1732 in
1733 k {New = !new, Old = !old, Outs = sis}
1734 end
1735 | AllCols k =>
1736 let
1737 val (ts, es) =
1738 foldl (fn (si, (ts, es)) =>
1739 case si of
1740 SqField (v, f) =>
1741 let
1742 val fs = getOpt (SM.find (ts, v), SM.empty)
1743 in
1744 (SM.insert (ts, v, SM.insert (fs, f, Proj (rvOf v, f))), es)
1745 end
1746 | SqExp (e, f) =>
1747 let
1748 val e =
1749 case expIn e of
1750 inr _ => #NextVar arg ()
1751 | inl e => e
1752 in
1753 (ts, SM.insert (es, f, e))
1754 end)
1755 (SM.empty, SM.empty) (#Select r)
1756 in
1757 k (Recd (map (fn (t, fs) => (t, Recd (SM.listItemsi fs)))
1758 (SM.listItemsi ts)
1759 @ SM.listItemsi es))
1760 end
1761
1762 fun doWhere final =
1763 (addFrom ();
1764 case #Where r of
1765 NONE => final ()
1766 | SOME e =>
1767 let
1768 val p = case expIn e of
1769 inl e => Reln (Eq, [e, Func (DtCon0 "Basis.bool.True", [])])
1770 | inr p => p
1771
1772 val saved = #Save arg ()
1773 in
1774 decomp {Save = #Save arg, Restore = #Restore arg, Add = #Add arg}
1775 p (fn () => final () handle Cc.Contradiction => ());
1776 #Restore arg saved
1777 end)
1778 handle Cc.Contradiction => ()
1779
1780 fun normal () = doWhere normal'
1781 in
1782 (case #Select r of
1783 [SqExp (Binop (Exps bo, Count, SqConst (Prim.Int 0)), f)] =>
1784 (case bo (Const (Prim.Int 1), Const (Prim.Int 2)) of
1785 Reln (Gt, [Const (Prim.Int 1), Const (Prim.Int 2)]) =>
1786 (case #Cont arg of
1787 SomeCol _ => ()
1788 | AllCols k =>
1789 let
1790 fun answer e = k (Recd [(f, e)])
1791
1792 val saved = #Save arg ()
1793 val () = (answer (Func (DtCon0 "Basis.bool.False", [])))
1794 handle Cc.Contradiction => ()
1795 in
1796 #Restore arg saved;
1797 (*print "True time!\n";*)
1798 doWhere (fn () => answer (Func (DtCon0 "Basis.bool.True", [])));
1799 #Restore arg saved
1800 end)
1801 | _ => normal ())
1802 | _ => normal ())
1803 before #Restore arg saved
1804 end
1805 | Union (q1, q2) =>
1806 let
1807 val saved = #Save arg ()
1808 in
1809 doQuery q1;
1810 #Restore arg saved;
1811 doQuery q2;
1812 #Restore arg saved
1813 end
1814 in
1815 doQuery q
1816 end
1817 end
1818
1819 fun evalPat env e (pt, _) =
1820 case pt of
1821 PWild => env
1822 | PVar _ => e :: env
1823 | PPrim _ => env
1824 | PCon (_, pc, NONE) => (St.assert [AReln (PCon0 (patCon pc), [e])]; env)
1825 | PCon (_, pc, SOME pt) =>
1826 let
1827 val env = evalPat env (Func (UnCon (patCon pc), [e])) pt
1828 in
1829 St.assert [AReln (PCon1 (patCon pc), [e])];
1830 env
1831 end
1832 | PRecord xpts =>
1833 foldl (fn ((x, pt, _), env) => evalPat env (Proj (e, x)) pt) env xpts
1834 | PNone _ => (St.assert [AReln (PCon0 "None", [e])]; env)
1835 | PSome (_, pt) =>
1836 let
1837 val env = evalPat env (Func (UnCon "Some", [e])) pt
1838 in
1839 St.assert [AReln (PCon1 "Some", [e])];
1840 env
1841 end
1842
1843 datatype arg_mode = Fixed | Decreasing | Arbitrary
1844 type rfun = {args : arg_mode list, tables : SS.set, cookies : SS.set, body : Mono.exp}
1845 val rfuns = ref (IM.empty : rfun IM.map)
1846
1847 fun evalExp env (e as (_, loc)) k =
1848 let
1849 (*val () = St.debug ()*)
1850 (*val () = Print.preface ("evalExp", MonoPrint.p_exp MonoEnv.empty e)*)
1851
1852 fun default () = k (Var (St.nextVar ()))
1853
1854 fun doFfi (m, s, es) =
1855 if m = "Basis" andalso SS.member (writers, s) then
1856 let
1857 fun doArgs es =
1858 case es of
1859 [] =>
1860 (if s = "set_cookie" then
1861 case es of
1862 [_, cname, _, _, _] =>
1863 (case #1 cname of
1864 EPrim (Prim.String cname) =>
1865 St.havocCookie cname
1866 | _ => ())
1867 | _ => ()
1868 else
1869 ();
1870 k (Recd []))
1871 | e :: es =>
1872 evalExp env e (fn e => (St.send (e, loc); doArgs es))
1873 in
1874 doArgs es
1875 end
1876 else if Settings.isEffectful (m, s) andalso not (Settings.isBenignEffectful (m, s)) then
1877 default ()
1878 else
1879 let
1880 fun doArgs (es, acc) =
1881 case es of
1882 [] => k (Func (Other (m ^ "." ^ s), rev acc))
1883 | e :: es =>
1884 evalExp env e (fn e => doArgs (es, e :: acc))
1885 in
1886 doArgs (es, [])
1887 end
1888 in
1889 case #1 e of
1890 EPrim p => k (Const p)
1891 | ERel n => k (List.nth (env, n))
1892 | ENamed _ => default ()
1893 | ECon (_, pc, NONE) => k (Func (DtCon0 (patCon pc), []))
1894 | ECon (_, pc, SOME e) => evalExp env e (fn e => k (Func (DtCon1 (patCon pc), [e])))
1895 | ENone _ => k (Func (DtCon0 "None", []))
1896 | ESome (_, e) => evalExp env e (fn e => k (Func (DtCon1 "Some", [e])))
1897 | EFfi _ => default ()
1898
1899 | EFfiApp ("Basis", "rand", []) =>
1900 let
1901 val e = Var (St.nextVar ())
1902 in
1903 St.assert [AReln (Known, [e])];
1904 k e
1905 end
1906 | EFfiApp x => doFfi x
1907 | EApp ((EFfi (m, s), _), e) => doFfi (m, s, [e])
1908
1909 | EApp (e1 as (EError _, _), _) => evalExp env e1 k
1910
1911 | EApp (e1, e2) =>
1912 let
1913 fun adefault () = (ErrorMsg.errorAt loc "Excessively fancy function call";
1914 Print.preface ("Call", MonoPrint.p_exp MonoEnv.empty e);
1915 default ())
1916
1917 fun doArgs (e, args) =
1918 case #1 e of
1919 EApp (e1, e2) => doArgs (e1, e2 :: args)
1920 | ENamed n =>
1921 (case IM.find (!rfuns, n) of
1922 NONE => adefault ()
1923 | SOME rf =>
1924 if length (#args rf) <> length args then
1925 adefault ()
1926 else
1927 let
1928 val () = (SS.app (St.havocReln o Sql) (#tables rf);
1929 SS.app St.havocCookie (#cookies rf))
1930 val saved = St.stash ()
1931
1932 fun doArgs (args, modes, env') =
1933 case (args, modes) of
1934 ([], []) => (evalExp env' (#body rf) (fn _ => ());
1935 St.reinstate saved;
1936 default ())
1937
1938 | (arg :: args, mode :: modes) =>
1939 evalExp env arg (fn arg =>
1940 let
1941 val v = case mode of
1942 Arbitrary => Var (St.nextVar ())
1943 | Fixed => arg
1944 | Decreasing =>
1945 let
1946 val v = Var (St.nextVar ())
1947 in
1948 if St.check (AReln (Known, [arg])) then
1949 St.assert [(AReln (Known, [v]))]
1950 else
1951 ();
1952 v
1953 end
1954 in
1955 doArgs (args, modes, v :: env')
1956 end)
1957 | _ => raise Fail "Iflow.doArgs: Impossible"
1958 in
1959 doArgs (args, #args rf, [])
1960 end)
1961 | _ => adefault ()
1962 in
1963 doArgs (e, [])
1964 end
1965
1966 | EAbs _ => default ()
1967 | EUnop (s, e1) => evalExp env e1 (fn e1 => k (Func (Other s, [e1])))
1968 | EBinop (s, e1, e2) => evalExp env e1 (fn e1 => evalExp env e2 (fn e2 => k (Func (Other s, [e1, e2]))))
1969 | ERecord xets =>
1970 let
1971 fun doFields (xes, acc) =
1972 case xes of
1973 [] => k (Recd (rev acc))
1974 | (x, e, _) :: xes =>
1975 evalExp env e (fn e => doFields (xes, (x, e) :: acc))
1976 in
1977 doFields (xets, [])
1978 end
1979 | EField (e, s) => evalExp env e (fn e => k (Proj (e, s)))
1980 | ECase (e, pes, {result = res, ...}) =>
1981 evalExp env e (fn e =>
1982 if List.all (fn (_, (EWrite (EPrim _, _), _)) => true
1983 | _ => false) pes then
1984 (St.send (e, loc);
1985 k (Recd []))
1986 else
1987 (St.addPath (e, loc);
1988 app (fn (p, pe) =>
1989 let
1990 val saved = St.stash ()
1991 in
1992 let
1993 val env = evalPat env e p
1994 in
1995 evalExp env pe k;
1996 St.reinstate saved
1997 end
1998 handle Cc.Contradiction => St.reinstate saved
1999 end) pes))
2000 | EStrcat (e1, e2) =>
2001 evalExp env e1 (fn e1 =>
2002 evalExp env e2 (fn e2 =>
2003 k (Func (Other "cat", [e1, e2]))))
2004 | EError (e, _) => evalExp env e (fn e => St.send (e, loc))
2005 | EReturnBlob {blob = b, mimeType = m, ...} =>
2006 evalExp env b (fn b =>
2007 (St.send (b, loc);
2008 evalExp env m
2009 (fn m => St.send (m, loc))))
2010 | ERedirect (e, _) =>
2011 evalExp env e (fn e => St.send (e, loc))
2012 | EWrite e =>
2013 evalExp env e (fn e => (St.send (e, loc);
2014 k (Recd [])))
2015 | ESeq (e1, e2) =>
2016 let
2017 val path = St.stashPath ()
2018 in
2019 evalExp env e1 (fn _ => (St.reinstatePath path; evalExp env e2 k))
2020 end
2021 | ELet (_, _, e1, e2) =>
2022 evalExp env e1 (fn e1 => evalExp (e1 :: env) e2 k)
2023 | EClosure (n, es) =>
2024 let
2025 fun doArgs (es, acc) =
2026 case es of
2027 [] => k (Func (Other ("Cl" ^ Int.toString n), rev acc))
2028 | e :: es =>
2029 evalExp env e (fn e => doArgs (es, e :: acc))
2030 in
2031 doArgs (es, [])
2032 end
2033
2034 | EQuery {query = q, body = b, initial = i, state = state, ...} =>
2035 evalExp env i (fn i =>
2036 let
2037 val r = Var (St.nextVar ())
2038 val acc = Var (St.nextVar ())
2039
2040 val (ts, cs) = MonoUtil.Exp.fold {typ = fn (_, st) => st,
2041 exp = fn (e, st as (cs, ts)) =>
2042 case e of
2043 EDml e =>
2044 (case parse dml e of
2045 NONE => st
2046 | SOME c =>
2047 case c of
2048 Insert _ => st
2049 | Delete (tab, _) =>
2050 (cs, SS.add (ts, tab))
2051 | Update (tab, _, _) =>
2052 (cs, SS.add (ts, tab)))
2053 | EFfiApp ("Basis", "set_cookie",
2054 [_, (EPrim (Prim.String cname), _),
2055 _, _, _]) =>
2056 (SS.add (cs, cname), ts)
2057 | _ => st}
2058 (SS.empty, SS.empty) b
2059 in
2060 case (#1 state, SS.isEmpty ts, SS.isEmpty cs) of
2061 (TRecord [], true, true) => ()
2062 | _ =>
2063 let
2064 val saved = St.stash ()
2065 in
2066 (k i)
2067 handle Cc.Contradiction => ();
2068 St.reinstate saved
2069 end;
2070
2071 SS.app (St.havocReln o Sql) ts;
2072 SS.app St.havocCookie cs;
2073
2074 doQuery {Env = env,
2075 NextVar = Var o St.nextVar,
2076 Add = fn a => St.assert [a],
2077 Save = St.stash,
2078 Restore = St.reinstate,
2079 Cont = AllCols (fn x =>
2080 (St.assert [AReln (Eq, [r, x])];
2081 evalExp (acc :: r :: env) b k))} q
2082 end)
2083 | EDml e =>
2084 (case parse dml e of
2085 NONE => (print ("Warning: Information flow checker can't parse DML command at "
2086 ^ ErrorMsg.spanToString loc ^ "\n");
2087 default ())
2088 | SOME d =>
2089 case d of
2090 Insert (tab, es) =>
2091 let
2092 val new = St.nextVar ()
2093
2094 val expIn = expIn (Var o St.nextVar) env
2095 (fn _ => raise Fail "Iflow.evalExp: Bad field expression in INSERT [1]")
2096
2097 val es = map (fn (x, e) =>
2098 case expIn e of
2099 inl e => (x, e)
2100 | inr _ => raise Fail "Iflow.evalExp: Bad field expression in INSERT [2]")
2101 es
2102
2103 val saved = St.stash ()
2104 in
2105 St.assert [AReln (Sql (tab ^ "$New"), [Recd es])];
2106 St.insert loc;
2107 St.reinstate saved;
2108 St.assert [AReln (Sql tab, [Recd es])];
2109 k (Recd [])
2110 end
2111 | Delete (tab, e) =>
2112 let
2113 val old = St.nextVar ()
2114
2115 val expIn = expIn (Var o St.nextVar) env
2116 (fn "T" => Var old
2117 | _ => raise Fail "Iflow.evalExp: Bad field expression in DELETE")
2118
2119 val p = case expIn e of
2120 inl e => raise Fail "Iflow.evalExp: DELETE with non-boolean"
2121 | inr p => p
2122
2123 val saved = St.stash ()
2124 in
2125 St.assert [AReln (Sql (tab ^ "$Old"), [Var old]),
2126 AReln (Sql (tab), [Var old])];
2127 decomp {Save = St.stash,
2128 Restore = St.reinstate,
2129 Add = fn a => St.assert [a]} p
2130 (fn () => (St.delete loc;
2131 St.reinstate saved;
2132 St.havocReln (Sql tab);
2133 k (Recd []))
2134 handle Cc.Contradiction => ())
2135 end
2136 | Update (tab, fs, e) =>
2137 let
2138 val new = St.nextVar ()
2139 val old = St.nextVar ()
2140
2141 val expIn = expIn (Var o St.nextVar) env
2142 (fn "T" => Var old
2143 | _ => raise Fail "Iflow.evalExp: Bad field expression in UPDATE")
2144
2145 val fs = map
2146 (fn (x, e) =>
2147 (x, case expIn e of
2148 inl e => e
2149 | inr _ => raise Fail
2150 ("Iflow.evalExp: Selecting "
2151 ^ "boolean expression")))
2152 fs
2153
2154 val fs' = case SM.find (!tabs, tab) of
2155 NONE => raise Fail "Iflow.evalExp: Updating unknown table"
2156 | SOME (fs', _) => fs'
2157
2158 val fs = foldl (fn (f, fs) =>
2159 if List.exists (fn (f', _) => f' = f) fs then
2160 fs
2161 else
2162 (f, Proj (Var old, f)) :: fs) fs fs'
2163
2164 val p = case expIn e of
2165 inl e => raise Fail "Iflow.evalExp: UPDATE with non-boolean"
2166 | inr p => p
2167 val saved = St.stash ()
2168 in
2169 St.assert [AReln (Sql (tab ^ "$New"), [Recd fs]),
2170 AReln (Sql (tab ^ "$Old"), [Var old]),
2171 AReln (Sql tab, [Var old])];
2172 decomp {Save = St.stash,
2173 Restore = St.reinstate,
2174 Add = fn a => St.assert [a]} p
2175 (fn () => (St.update loc;
2176 St.reinstate saved;
2177 St.havocReln (Sql tab);
2178 k (Recd []))
2179 handle Cc.Contradiction => ())
2180 end)
2181
2182 | ENextval (EPrim (Prim.String seq), _) =>
2183 let
2184 val nv = St.nextVar ()
2185 in
2186 St.assert [AReln (Sql (String.extract (seq, 3, NONE)), [Var nv])];
2187 k (Var nv)
2188 end
2189 | ENextval _ => default ()
2190 | ESetval _ => default ()
2191
2192 | EUnurlify ((EFfiApp ("Basis", "get_cookie", [(EPrim (Prim.String cname), _)]), _), _, _) =>
2193 let
2194 val e = Var (St.nextVar ())
2195 val e' = Func (Other ("cookie/" ^ cname), [])
2196 in
2197 St.assert [AReln (Known, [e]), AReln (Eq, [e, e'])];
2198 k e
2199 end
2200
2201 | EUnurlify _ => default ()
2202 | EJavaScript _ => default ()
2203 | ESignalReturn _ => default ()
2204 | ESignalBind _ => default ()
2205 | ESignalSource _ => default ()
2206 | EServerCall _ => default ()
2207 | ERecv _ => default ()
2208 | ESleep _ => default ()
2209 | ESpawn _ => default ()
2210 end
2211
2212 datatype var_source = Input of int | SubInput of int | Unknown
2213
2214 fun check file =
2215 let
2216 val () = (St.reset ();
2217 rfuns := IM.empty)
2218
2219 val file = MonoReduce.reduce file
2220 val file = MonoOpt.optimize file
2221 val file = Fuse.fuse file
2222 val file = MonoOpt.optimize file
2223 val file = MonoShake.shake file
2224 (*val () = Print.preface ("File", MonoPrint.p_file MonoEnv.empty file)*)
2225
2226 val exptd = foldl (fn ((d, _), exptd) =>
2227 case d of
2228 DExport (_, _, n, _, _, _) => IS.add (exptd, n)
2229 | _ => exptd) IS.empty file
2230
2231 fun decl (d, loc) =
2232 case d of
2233 DTable (tab, fs, pk, _) =>
2234 let
2235 val ks =
2236 case #1 pk of
2237 EPrim (Prim.String s) =>
2238 (case String.tokens (fn ch => ch = #"," orelse ch = #" ") s of
2239 [] => []
2240 | pk => [pk])
2241 | _ => []
2242 in
2243 if size tab >= 3 then
2244 tabs := SM.insert (!tabs, String.extract (tab, 3, NONE),
2245 (map #1 fs,
2246 map (map (fn s => str (Char.toUpper (String.sub (s, 3)))
2247 ^ String.extract (s, 4, NONE))) ks))
2248 else
2249 raise Fail "Table name does not begin with uw_"
2250 end
2251 | DVal (x, n, _, e, _) =>
2252 let
2253 (*val () = print ("\n=== " ^ x ^ " ===\n\n");*)
2254
2255 val isExptd = IS.member (exptd, n)
2256
2257 val saved = St.stash ()
2258
2259 fun deAbs (e, env, ps) =
2260 case #1 e of
2261 EAbs (_, _, _, e) =>
2262 let
2263 val nv = Var (St.nextVar ())
2264 in
2265 deAbs (e, nv :: env,
2266 if isExptd then
2267 AReln (Known, [nv]) :: ps
2268 else
2269 ps)
2270 end
2271 | _ => (e, env, ps)
2272
2273 val (e, env, ps) = deAbs (e, [], [])
2274 in
2275 St.assert ps;
2276 (evalExp env e (fn _ => ()) handle Cc.Contradiction => ());
2277 St.reinstate saved
2278 end
2279
2280 | DValRec [(x, n, _, e, _)] =>
2281 let
2282 val tables = ref SS.empty
2283 val cookies = ref SS.empty
2284
2285 fun deAbs (e, env, modes) =
2286 case #1 e of
2287 EAbs (_, _, _, e) => deAbs (e, Input (length env) :: env, ref Fixed :: modes)
2288 | _ => (e, env, rev modes)
2289
2290 val (e, env, modes) = deAbs (e, [], [])
2291
2292 fun doExp env (e as (_, loc)) =
2293 case #1 e of
2294 EPrim _ => e
2295 | ERel _ => e
2296 | ENamed _ => e
2297 | ECon (_, _, NONE) => e
2298 | ECon (dk, pc, SOME e) => (ECon (dk, pc, SOME (doExp env e)), loc)
2299 | ENone _ => e
2300 | ESome (t, e) => (ESome (t, doExp env e), loc)
2301 | EFfi _ => e
2302 | EFfiApp (m, f, es) =>
2303 (case (m, f, es) of
2304 ("Basis", "set_cookie", [_, (EPrim (Prim.String cname), _), _, _, _]) =>
2305 cookies := SS.add (!cookies, cname)
2306 | _ => ();
2307 (EFfiApp (m, f, map (doExp env) es), loc))
2308
2309 | EApp (e1, e2) =>
2310 let
2311 fun default () = (EApp (doExp env e1, doExp env e2), loc)
2312
2313 fun explore (e, args) =
2314 case #1 e of
2315 EApp (e1, e2) => explore (e1, e2 :: args)
2316 | ENamed n' =>
2317 if n' = n then
2318 let
2319 fun doArgs (pos, args, modes) =
2320 case (args, modes) of
2321 ((e1, _) :: args, m1 :: modes) =>
2322 (case e1 of
2323 ERel n =>
2324 (case List.nth (env, n) of
2325 Input pos' =>
2326 if pos' = pos then
2327 ()
2328 else
2329 m1 := Arbitrary
2330 | SubInput pos' =>
2331 if pos' = pos then
2332 if !m1 = Arbitrary then
2333 ()
2334 else
2335 m1 := Decreasing
2336 else
2337 m1 := Arbitrary
2338 | Unknown => m1 := Arbitrary)
2339 | _ => m1 := Arbitrary;
2340 doArgs (pos + 1, args, modes))
2341 | (_ :: _, []) => ()
2342 | ([], ms) => app (fn m => m := Arbitrary) ms
2343 in
2344 doArgs (0, args, modes);
2345 (EFfi ("Basis", "?"), loc)
2346 end
2347 else
2348 default ()
2349 | _ => default ()
2350 in
2351 explore (e, [])
2352 end
2353 | EAbs (x, t1, t2, e) => (EAbs (x, t1, t2, doExp (Unknown :: env) e), loc)
2354 | EUnop (uo, e1) => (EUnop (uo, doExp env e1), loc)
2355 | EBinop (bo, e1, e2) => (EBinop (bo, doExp env e1, doExp env e2), loc)
2356 | ERecord xets => (ERecord (map (fn (x, e, t) => (x, doExp env e, t)) xets), loc)
2357 | EField (e1, f) => (EField (doExp env e1, f), loc)
2358 | ECase (e, pes, ts) =>
2359 let
2360 val source =
2361 case #1 e of
2362 ERel n =>
2363 (case List.nth (env, n) of
2364 Input n => SOME n
2365 | SubInput n => SOME n
2366 | Unknown => NONE)
2367 | _ => NONE
2368
2369 fun doV v =
2370 let
2371 fun doPat (p, env) =
2372 case #1 p of
2373 PWild => env
2374 | PVar _ => v :: env
2375 | PPrim _ => env
2376 | PCon (_, _, NONE) => env
2377 | PCon (_, _, SOME p) => doPat (p, env)
2378 | PRecord xpts => foldl (fn ((_, p, _), env) => doPat (p, env)) env xpts
2379 | PNone _ => env
2380 | PSome (_, p) => doPat (p, env)
2381 in
2382 (ECase (e, map (fn (p, e) => (p, doExp (doPat (p, env)) e)) pes, ts), loc)
2383 end
2384 in
2385 case source of
2386 NONE => doV Unknown
2387 | SOME inp => doV (SubInput inp)
2388 end
2389 | EStrcat (e1, e2) => (EStrcat (doExp env e1, doExp env e2), loc)
2390 | EError (e1, t) => (EError (doExp env e1, t), loc)
2391 | EReturnBlob {blob = b, mimeType = m, t} =>
2392 (EReturnBlob {blob = doExp env b, mimeType = doExp env m, t = t}, loc)
2393 | ERedirect (e1, t) => (ERedirect (doExp env e1, t), loc)
2394 | EWrite e1 => (EWrite (doExp env e1), loc)
2395 | ESeq (e1, e2) => (ESeq (doExp env e1, doExp env e2), loc)
2396 | ELet (x, t, e1, e2) => (ELet (x, t, doExp env e1, doExp (Unknown :: env) e2), loc)
2397 | EClosure (n, es) => (EClosure (n, map (doExp env) es), loc)
2398 | EQuery {exps, tables, state, query, body, initial} =>
2399 (EQuery {exps = exps, tables = tables, state = state,
2400 query = doExp env query,
2401 body = doExp (Unknown :: Unknown :: env) body,
2402 initial = doExp env initial}, loc)
2403 | EDml e1 =>
2404 (case parse dml e1 of
2405 NONE => ()
2406 | SOME c =>
2407 case c of
2408 Insert _ => ()
2409 | Delete (tab, _) =>
2410 tables := SS.add (!tables, tab)
2411 | Update (tab, _, _) =>
2412 tables := SS.add (!tables, tab);
2413 (EDml (doExp env e1), loc))
2414 | ENextval e1 => (ENextval (doExp env e1), loc)
2415 | ESetval (e1, e2) => (ESetval (doExp env e1, doExp env e2), loc)
2416 | EUnurlify (e1, t, b) => (EUnurlify (doExp env e1, t, b), loc)
2417 | EJavaScript (m, e) => (EJavaScript (m, doExp env e), loc)
2418 | ESignalReturn _ => e
2419 | ESignalBind _ => e
2420 | ESignalSource _ => e
2421 | EServerCall _ => e
2422 | ERecv _ => e
2423 | ESleep _ => e
2424 | ESpawn _ => e
2425
2426 val e = doExp env e
2427 in
2428 rfuns := IM.insert (!rfuns, n, {tables = !tables, cookies = !cookies,
2429 args = map (fn r => !r) modes, body = e})
2430 end
2431
2432 | DValRec _ => ErrorMsg.errorAt loc "Iflow can't check mutually-recursive functions yet."
2433
2434 | DPolicy pol =>
2435 let
2436 val rvN = ref 0
2437 fun rv () =
2438 let
2439 val n = !rvN
2440 in
2441 rvN := n + 1;
2442 Lvar n
2443 end
2444
2445 val atoms = ref ([] : atom list)
2446 fun doQ k = doQuery {Env = [],
2447 NextVar = rv,
2448 Add = fn a => atoms := a :: !atoms,
2449 Save = fn () => !atoms,
2450 Restore = fn ls => atoms := ls,
2451 Cont = SomeCol (fn r => k (rev (!atoms), r))}
2452
2453 fun untab (tab, nams) = List.filter (fn AReln (Sql tab', [Lvar lv]) =>
2454 tab' <> tab
2455 orelse List.all (fn Lvar lv' => lv' <> lv
2456 | _ => false) nams
2457 | _ => true)
2458 in
2459 case pol of
2460 PolClient e =>
2461 doQ (fn (ats, {Outs = es, ...}) => St.allowSend (ats, es)) e
2462 | PolInsert e =>
2463 doQ (fn (ats, {New = SOME (tab, new), ...}) =>
2464 St.allowInsert (AReln (Sql (tab ^ "$New"), [new]) :: untab (tab, [new]) ats)
2465 | _ => raise Fail "Iflow: No New in mayInsert policy") e
2466 | PolDelete e =>
2467 doQ (fn (ats, {Old = SOME (tab, old), ...}) =>
2468 St.allowDelete (AReln (Sql (tab ^ "$Old"), [old]) :: untab (tab, [old]) ats)
2469 | _ => raise Fail "Iflow: No Old in mayDelete policy") e
2470 | PolUpdate e =>
2471 doQ (fn (ats, {New = SOME (tab, new), Old = SOME (_, old), ...}) =>
2472 St.allowUpdate (AReln (Sql (tab ^ "$Old"), [old])
2473 :: AReln (Sql (tab ^ "$New"), [new])
2474 :: untab (tab, [new, old]) ats)
2475 | _ => raise Fail "Iflow: No New or Old in mayUpdate policy") e
2476 | PolSequence e =>
2477 (case #1 e of
2478 EPrim (Prim.String seq) =>
2479 let
2480 val p = AReln (Sql (String.extract (seq, 3, NONE)), [Lvar 0])
2481 val outs = [Lvar 0]
2482 in
2483 St.allowSend ([p], outs)
2484 end
2485 | _ => ())
2486 end
2487
2488 | _ => ()
2489 in
2490 app decl file
2491 end
2492
2493 val check = fn file =>
2494 let
2495 val oldInline = Settings.getMonoInline ()
2496 in
2497 (Settings.setMonoInline (case Int.maxInt of
2498 NONE => 1000000
2499 | SOME n => n);
2500 check file;
2501 Settings.setMonoInline oldInline)
2502 handle ex => (Settings.setMonoInline oldInline;
2503 raise ex)
2504 end
2505
2506 end