view src/checknest.sml @ 1691:ea292bf9431f

Avoid URL size limit in RPCs with large arguments
author Adam Chlipala <adam@chlipala.net>
date Sat, 03 Mar 2012 16:41:20 -0500
parents 0577be31a435
children 98895243b5b6
line wrap: on
line source
(* Copyright (c) 2009, Adam Chlipala
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - The names of contributors may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *)

structure Checknest :> CHECKNEST = struct

open Cjr

structure IS = IntBinarySet
structure IM = IntBinaryMap

fun expUses globals =
    let
        fun eu (e, _) =
            case e of
                EPrim _ => IS.empty
              | ERel _ => IS.empty
              | ENamed n => Option.getOpt (IM.find (globals, n), IS.empty)
              | ECon (_, _, NONE) => IS.empty
              | ECon (_, _, SOME e) => eu e
              | ENone _ => IS.empty
              | ESome (_, e) => eu e
              | EFfi _ => IS.empty
              | EFfiApp (_, _, es) => foldl IS.union IS.empty (map (eu o #1) es)
              | EApp (e, es) => foldl IS.union (eu e) (map eu es)

              | EUnop (_, e) => eu e
              | EBinop (_, e1, e2) => IS.union (eu e1, eu e2)

              | ERecord (_, xes) => foldl (fn ((_, e), s) => IS.union (eu e, s)) IS.empty xes
              | EField (e, _) => eu e

              | ECase (e, pes, _) => foldl (fn ((_, e), s) => IS.union (eu e, s)) (eu e) pes

              | EError (e, _) => eu e
              | EReturnBlob {blob, mimeType, ...} => IS.union (eu blob, eu mimeType)
              | ERedirect (e, _) => eu e

              | EWrite e => eu e
              | ESeq (e1, e2) => IS.union (eu e1, eu e2)
              | ELet (_, _, e1, e2) => IS.union (eu e1, eu e2)

              | EQuery {query, body, initial, prepared, ...} =>
                let
                    val s = IS.union (eu query, IS.union (eu body, eu initial))
                in
                    case prepared of
                        SOME {id, ...} => IS.add (s, id)
                      | _ => s
                end
              | EDml {dml, prepared, ...} =>
                let
                    val s = eu dml
                in
                    case prepared of
                        SOME {id, ...} => IS.add (s, id)
                      | _ => s
                end
              | ENextval {seq, prepared, ...} =>
                let
                    val s = eu seq
                in
                    case prepared of
                        SOME {id, ...} => IS.add (s, id)
                      | _ => s
                end
              | ESetval {seq, count} => IS.union (eu seq, eu count)

              | EUnurlify (e, _, _) => eu e
    in
        eu
    end

fun annotateExp globals =
    let
        fun ae (e as (_, loc)) =
            case #1 e of
                EPrim _ => e
              | ERel _ => e
              | ENamed n => e
              | ECon (_, _, NONE) => e
              | ECon (dk, pc, SOME e) => (ECon (dk, pc, SOME (ae e)), loc)
              | ENone _ => e
              | ESome (t, e) => (ESome (t, ae e), loc)
              | EFfi _ => e
              | EFfiApp (m, f, es) => (EFfiApp (m, f, map (fn (e, t) => (ae e, t)) es), loc)
              | EApp (e, es) => (EApp (ae e, map ae es), loc)

              | EUnop (uo, e) => (EUnop (uo, ae e), loc)
              | EBinop (bo, e1, e2) => (EBinop (bo, ae e1, ae e2), loc)

              | ERecord (n, xes) => (ERecord (n, map (fn (x, e) => (x, ae e)) xes), loc)
              | EField (e, f) => (EField (ae e, f), loc)

              | ECase (e, pes, ts) => (ECase (ae e, map (fn (p, e) => (p, ae e)) pes, ts), loc)

              | EError (e, t) => (EError (ae e, t), loc)
              | EReturnBlob {blob, mimeType, t} => (EReturnBlob {blob = ae blob, mimeType = ae mimeType, t = t}, loc)
              | ERedirect (e, t) => (ERedirect (ae e, t), loc)

              | EWrite e => (EWrite (ae e), loc)
              | ESeq (e1, e2) => (ESeq (ae e1, ae e2), loc)
              | ELet (x, t, e1, e2) => (ELet (x, t, ae e1, ae e2), loc)

              | EQuery {exps, tables, rnum, state, query, body, initial, prepared} =>
                (EQuery {exps = exps,
                         tables = tables,
                         rnum = rnum,
                         state = state,
                         query = ae query,
                         body = ae body,
                         initial = ae initial,
                         prepared = case prepared of
                                        NONE => NONE
                                      | SOME {id, query, ...} => SOME {id = id, query = query,
                                                                       nested = IS.member (expUses globals body, id)}},
                 loc)
              | EDml {dml, prepared, mode} =>
                (EDml {dml = ae dml,
                       prepared = prepared,
                       mode = mode}, loc)

              | ENextval {seq, prepared} =>
                (ENextval {seq = ae seq,
                           prepared = prepared}, loc)
              | ESetval {seq, count} =>
                (ESetval {seq = ae seq,
                          count = ae count}, loc)

              | EUnurlify (e, t, b) => (EUnurlify (ae e, t, b), loc)
    in
        ae
    end

fun annotate (ds, syms) =
    let
        val globals =
            foldl (fn ((d, _), globals) =>
                      case d of
                          DVal (_, n, _, e) => IM.insert (globals, n, expUses globals e)
                        | DFun (_, n, _, _, e) => IM.insert (globals, n, expUses globals e)
                        | DFunRec fs =>
                          let
                              val s = foldl (fn ((_, _, _, _, e), s) => IS.union (expUses globals e, s)) IS.empty fs
                          in
                              foldl (fn ((_, n, _, _, _), globals) => IM.insert (globals, n, s)) globals fs
                          end
                        | _ => globals) IM.empty ds

        val ds =
            map (fn d as (_, loc) =>
                    case #1 d of
                        DVal (x, n, t, e) => (DVal (x, n, t, annotateExp globals e), loc)
                      | DFun (x, n, ts, t, e) => (DFun (x, n, ts, t, annotateExp globals e), loc)
                      | DFunRec fs => (DFunRec
                                           (map (fn (x, n, ts, t, e) => (x, n, ts, t, annotateExp globals e)) fs), loc)
                      | _ => d) ds
    in
        (ds, syms)
    end

end