# HG changeset patch # User Adam Chlipala # Date 1236696266 14400 # Node ID aa2290c32ce29af8a46253b629c9fcbe63acf5d6 # Parent 4a125bbc602d7eb3e392b9fe7dad767b0a563933 Avoid any JavaScript when pages don't need it; update demo prose diff -r 4a125bbc602d -r aa2290c32ce2 demo/metaform.ur --- a/demo/metaform.ur Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/metaform.ur Tue Mar 10 10:44:26 2009 -0400 @@ -1,7 +1,7 @@ functor Make (M : sig con fs :: {Unit} val fl : folder fs - val names : $(mapUT string fs) + val names : $(mapU string fs) end) = struct fun handler values = return @@ -14,9 +14,9 @@ fun main () = return
- {foldUR [string] [fn cols :: {Unit} => xml form [] (mapUT string cols)] + {foldUR [string] [fn cols :: {Unit} => xml form [] (mapU string cols)] (fn (nm :: Name) (rest :: {Unit}) [[nm] ~ rest] name - (acc : xml form [] (mapUT string rest)) => + (acc : xml form [] (mapU string rest)) =>
  • {[name]}:
  • {useMore acc}
    ) diff -r 4a125bbc602d -r aa2290c32ce2 demo/metaform.urs --- a/demo/metaform.urs Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/metaform.urs Tue Mar 10 10:44:26 2009 -0400 @@ -1,7 +1,7 @@ functor Make (M : sig con fs :: {Unit} val fl : folder fs - val names : $(mapUT string fs) + val names : $(mapU string fs) end) : sig val main : unit -> transaction page end diff -r 4a125bbc602d -r aa2290c32ce2 demo/prose --- a/demo/prose Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/prose Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,4 @@ -

    Ur/Web is a domain-specific language for programming web applications backed by SQL databases. It is (strongly) statically-typed (like ML and Haskell) and purely functional (like Haskell). Ur is the base language, and the web-specific features of Ur/Web (mostly) come only in the form of special rules for parsing and optimization. The Ur core looks a lot like Standard ML, with a few Haskell-isms added, and kinder, gentler versions added of many features from dependently-typed languages like the logic behind Coq. The type system is much more expressive than in ML and Haskell, such that well-typed web applications cannot "go wrong," not just in handling single HTTP requests, but across their entire lifetimes of interacting with HTTP clients. Beyond that, Ur is unusual is using ideas from dependent typing to enable very effective metaprogramming, or programming with explicit analysis of type structure. Many common web application components can be built by Ur/Web functions that operate on types, where it seems impossible to achieve similar code re-use in more established languages.

    +

    Ur/Web is a domain-specific language for programming web applications backed by SQL databases. It is (strongly) statically-typed (like ML and Haskell) and purely functional (like Haskell). Ur is the base language, and the web-specific features of Ur/Web (mostly) come only in the form of special rules for parsing and optimization. The Ur core looks a lot like Standard ML, with a few Haskell-isms added, and kinder, gentler versions added of many features from dependently-typed languages like the logic behind Coq. The type system is much more expressive than in ML and Haskell, such that well-typed web applications cannot "go wrong," not just in handling single HTTP requests, but across their entire lifetimes of interacting with HTTP clients. Beyond that, Ur is unusual in using ideas from dependent typing to enable very effective metaprogramming, or programming with explicit analysis of type structure. Many common web application components can be built by Ur/Web functions that operate on types, where it seems impossible to achieve similar code re-use in more established statically-typed languages.

    This demo is built automatically from Ur/Web sources and supporting files. If you unpack the Ur/Web source distribution, then the following steps will build you a local version of this demo: @@ -92,19 +92,33 @@

    The functor creates a new encapsulated SQL sequence and table on each call. These local relations show up in the automatically-generated SQL file that should be run to prepare the database for use, but they are invisible from client code. We could change the functor to create different SQL relations, without needing to change client code.

    +

    Note that, in ref.ur, the inj components of functor arguments are omitted. Since these arguments are type class witnesses, the compiler infers them automatically based on the choices of data.

    + tree.urp

    Here we see how we can abstract over common patterns of SQL queries. In particular, since standard SQL does not help much with queries over trees, we write a function for traversing an SQL tree, building an HTML representation, based on a user-provided function for rendering individual rows.

    +

    The signature of TreeFun.Make tells us that, to instantiate the functor, we must provide

    +
      +
    1. A primary key type key
    2. +
    3. SQL field names id (for primary keys) and parent (for parent links)
    4. +
    5. A type-level record cols of field names besides id and parent
    6. +
    7. "Proofs" that id is distinct from parent and that neither of id and parent appears in cols
    8. +
    9. Witnesses that both key and option key belong to the type class sql_injectable, which indicates that they are fair game to use with SQL
    10. +
    11. An SQL table tab, containing a field id of type key, a field parent of type option key, and every field of cols
    12. +
    + sum.urp

    Metaprogramming is one of the most important facilities of Ur. This example shows how to write a function that is able to sum up the fields of records of integers, no matter which set of fields the particular record has.

    Ur's support for analysis of types is based around extensible records, or row types. In the definition of the sum function, we see the type parameter fs assigned the kind {Unit}, which stands for records of types of kind Unit. The Unit kind has only one inhabitant, (). The kind Type is for "normal" types.

    -

    The unary $ operator is used to build a record Type from a {Type} (that is, the kind of records of types). The library function mapUT takes in a Type t and a {Unit} r, and it builds a {Type} as long as r, where every field is assigned value t.

    +

    The sum function also takes an argument fl of type folder fs. Folders represent permutations of the elements of type-level records. We can use a folder to iterate over a type-level record in the order indicated by the permutation.

    -

    Another library function foldUR is defined at the level of expressions, while mapUT is a type-level function. foldUR takes 6 arguments, some of them types and some values. Type arguments are distinguished by being written within brackets. The arguments to foldUR respectively tell us: +

    The unary $ operator is used to build a record Type from a {Type} (that is, the kind of records of types). The library function mapU takes in a type t of kind K and a {Unit} r, and it builds a {K} as long as r, where every field is assigned value t.

    + +

    Another library function foldUR is defined at the level of expressions, while mapU is a type-level function. foldUR takes 7 arguments, some of them types and some values. Type arguments are distinguished by being written within brackets. The arguments to foldUR respectively tell us:

    1. The type we will assign to each record field
    2. @@ -112,6 +126,7 @@
    3. A function that updates the accumulator based on the current record field name, the rest of the input record type, the current record field value, and the current accumulator
    4. The initial accumulator value
    5. The input record type
    6. +
    7. A folder for that type
    8. The input record value
    @@ -119,7 +134,7 @@

    The general syntax for constant row types is [Name1 = t1, ..., NameN = tN], and there is a shorthand version [Name1, ..., NameN] for records of Units.

    -

    With sum defined, it is easy to make some sample calls. The form of the code for main does not make it apparent, but the compiler must "reverse engineer" the appropriate {Unit} from the {Type} available from the context at each call to sum.

    +

    With sum defined, it is easy to make some sample calls. The form of the code for main does not make it apparent, but the compiler must "reverse engineer" the appropriate {Unit} from the {Type} available from the context at each call to sum. The compiler also infers a folder for each call, guessing at the desired permutations by examining the orders in which field names are written in the code.

    tcSum.urp @@ -129,8 +144,8 @@

    We can use metaprogramming with row types to build HTML forms (and their handlers) generically. The functor Metaform.Make takes in a unit row fs and a value-level record names assigning string names to the fields of fs. The functor implementation builds a form handler with a library function foldURX2, which runs over two value-level records in parallel, building an XML fragment.

    -

    The form itself is generated using the more primitive foldUR. We see the type xml form [] (mapUT string cols) as the result of the fold. This is the type of XML fragments that are suitable for inclusion in forms, require no form fields to be defined on entry, and themselves define form fields whose names and types are given by mapUT string cols. The useMore function "weakens" the type of an XML fragment, so that it "pretends" to require additional fields as input. This weakening is necessary to accommodate the general typing rule for concatenating bits of XML. -

    The functor use in Metaform1 is trivial. The compiler infers the value of the structure member fs from the type of the value provided for names.

    +

    The form itself is generated using the more primitive foldUR. We see the type xml form [] (mapU string cols) as the result of the fold. This is the type of XML fragments that are suitable for inclusion in forms, require no form fields to be defined on entry, and themselves define form fields whose names and types are given by mapU string cols. The useMore function "weakens" the type of an XML fragment, so that it "pretends" to require additional fields as input. This weakening is necessary to accommodate the general typing rule for concatenating bits of XML. +

    The functor use in Metaform1 is trivial. The compiler infers the values of the structure members fs and fl from the type of the value provided for names.

    metaform2.urp @@ -165,7 +180,7 @@

    Looking at crud1.ur, we see that a use of the functor is almost trivial. Only the value components of the argument structure must be provided. The column row type is inferred, and the disjointness constraint is proved automatically.

    -

    We won't go into detail on the implementation of Crud.Make. The types of the functions used there can be found in the signatures of the built-in Basis module and the Top module from the standard library. The signature of the first and the signature and implementation of the second can be found in the lib directory of the Ur/Web distribution.

    +

    We won't go into detail on the implementation of Crud.Make. The types of the functions used there can be found in the signatures of the built-in Basis module and the Top module from the standard library. The signature of the first and the signature and implementation of the second can be found in the lib/ur directory of the Ur/Web distribution.

    crud2.urp diff -r 4a125bbc602d -r aa2290c32ce2 demo/ref.ur --- a/demo/ref.ur Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/ref.ur Tue Mar 10 10:44:26 2009 -0400 @@ -1,9 +1,9 @@ structure IR = RefFun.Make(struct - type t = int + type data = int end) structure SR = RefFun.Make(struct - type t = string + type data = string end) fun main () = diff -r 4a125bbc602d -r aa2290c32ce2 demo/sql.urp --- a/demo/sql.urp Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/sql.urp Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,3 @@ -debug database dbname=test sql sql.sql diff -r 4a125bbc602d -r aa2290c32ce2 demo/sum.ur --- a/demo/sum.ur Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/sum.ur Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,4 @@ -fun sum (fs ::: {Unit}) (fl : folder fs) (x : $(mapUT int fs)) = +fun sum (fs ::: {Unit}) (fl : folder fs) (x : $(mapU int fs)) = foldUR [int] [fn _ => int] (fn (nm :: Name) (rest :: {Unit}) [[nm] ~ rest] n acc => n + acc) 0 [fs] fl x diff -r 4a125bbc602d -r aa2290c32ce2 demo/tcSum.ur --- a/demo/tcSum.ur Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/tcSum.ur Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,4 @@ -fun sum (t ::: Type) (_ : num t) (fs ::: {Unit}) (fl : folder fs) (x : $(mapUT t fs)) = +fun sum (t ::: Type) (_ : num t) (fs ::: {Unit}) (fl : folder fs) (x : $(mapU t fs)) = foldUR [t] [fn _ => t] (fn (nm :: Name) (rest :: {Unit}) [[nm] ~ rest] n acc => n + acc) zero [fs] fl x diff -r 4a125bbc602d -r aa2290c32ce2 demo/tree.urp --- a/demo/tree.urp Sun Mar 08 20:34:21 2009 -0400 +++ b/demo/tree.urp Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,3 @@ -debug database dbname=test sql tree.sql diff -r 4a125bbc602d -r aa2290c32ce2 include/urweb.h --- a/include/urweb.h Sun Mar 08 20:34:21 2009 -0400 +++ b/include/urweb.h Tue Mar 10 10:44:26 2009 -0400 @@ -39,6 +39,7 @@ uw_Basis_int uw_Basis_new_client_source(uw_context, uw_Basis_string); uw_unit uw_Basis_set_client_source(uw_context, uw_Basis_int, uw_Basis_string); +void uw_set_script_header(uw_context, const char*); char *uw_Basis_get_script(uw_context, uw_unit); char *uw_Basis_htmlifyInt(uw_context, uw_Basis_int); diff -r 4a125bbc602d -r aa2290c32ce2 lib/ur/top.ur --- a/lib/ur/top.ur Sun Mar 08 20:34:21 2009 -0400 +++ b/lib/ur/top.ur Tue Mar 10 10:44:26 2009 -0400 @@ -55,7 +55,7 @@ con snd3 = K1 ==> K2 ==> K3 ==> fn t :: (K1 * K2 * K3) => t.2 con thd3 = K1 ==> K2 ==> K3 ==> fn t :: (K1 * K2 * K3) => t.3 -con mapUT = fn f :: Type => map (fn _ :: Unit => f) +con mapU = K ==> fn f :: K => map (fn _ :: Unit => f) con ex = fn tf :: (Type -> Type) => res ::: Type -> (choice :: Type -> tf choice -> res) -> res @@ -75,7 +75,7 @@ -> [[nm] ~ rest] => tf -> tr rest -> tr ([nm] ++ rest)) (i : tr []) (r :: {Unit}) (fold : folder r)= - fold [fn r :: {Unit} => $(mapUT tf r) -> tr r] + fold [fn r :: {Unit} => $(mapU tf r) -> tr r] (fn (nm :: Name) (t :: Unit) (rest :: {Unit}) acc [[nm] ~ rest] r => f [nm] [rest] ! r.nm (acc (r -- nm))) @@ -86,7 +86,7 @@ -> [[nm] ~ rest] => tf1 -> tf2 -> tr rest -> tr ([nm] ++ rest)) (i : tr []) (r :: {Unit}) (fold : folder r) = - fold [fn r :: {Unit} => $(mapUT tf1 r) -> $(mapUT tf2 r) -> tr r] + fold [fn r :: {Unit} => $(mapU tf1 r) -> $(mapU tf2 r) -> tr r] (fn (nm :: Name) (t :: Unit) (rest :: {Unit}) acc [[nm] ~ rest] r1 r2 => f [nm] [rest] ! r1.nm r2.nm (acc (r1 -- nm) (r2 -- nm))) diff -r 4a125bbc602d -r aa2290c32ce2 lib/ur/top.urs --- a/lib/ur/top.urs Sun Mar 08 20:34:21 2009 -0400 +++ b/lib/ur/top.urs Tue Mar 10 10:44:26 2009 -0400 @@ -29,7 +29,7 @@ con snd3 = K1 ==> K2 ==> K3 ==> fn t :: (K1 * K2 * K3) => t.2 con thd3 = K1 ==> K2 ==> K3 ==> fn t :: (K1 * K2 * K3) => t.3 -con mapUT = fn f :: Type => map (fn _ :: Unit => f) +con mapU = K ==> fn f :: K => map (fn _ :: Unit => f) con ex = fn tf :: (Type -> Type) => res ::: Type -> (choice :: Type -> tf choice -> res) -> res @@ -46,19 +46,19 @@ -> (nm :: Name -> rest :: {Unit} -> [[nm] ~ rest] => tf -> tr rest -> tr ([nm] ++ rest)) - -> tr [] -> r :: {Unit} -> folder r -> $(mapUT tf r) -> tr r + -> tr [] -> r :: {Unit} -> folder r -> $(mapU tf r) -> tr r val foldUR2 : tf1 :: Type -> tf2 :: Type -> tr :: ({Unit} -> Type) -> (nm :: Name -> rest :: {Unit} -> [[nm] ~ rest] => tf1 -> tf2 -> tr rest -> tr ([nm] ++ rest)) - -> tr [] -> r :: {Unit} -> folder r -> $(mapUT tf1 r) -> $(mapUT tf2 r) -> tr r + -> tr [] -> r :: {Unit} -> folder r -> $(mapU tf1 r) -> $(mapU tf2 r) -> tr r val foldURX2: tf1 :: Type -> tf2 :: Type -> ctx :: {Unit} -> (nm :: Name -> rest :: {Unit} -> [[nm] ~ rest] => tf1 -> tf2 -> xml ctx [] []) - -> r :: {Unit} -> folder r -> $(mapUT tf1 r) -> $(mapUT tf2 r) -> xml ctx [] [] + -> r :: {Unit} -> folder r -> $(mapU tf1 r) -> $(mapU tf2 r) -> xml ctx [] [] val foldR : K --> tf :: (K -> Type) -> tr :: ({K} -> Type) -> (nm :: Name -> t :: K -> rest :: {K} diff -r 4a125bbc602d -r aa2290c32ce2 src/c/urweb.c --- a/src/c/urweb.c Sun Mar 08 20:34:21 2009 -0400 +++ b/src/c/urweb.c Tue Mar 10 10:44:26 2009 -0400 @@ -42,6 +42,8 @@ cleanup *cleanup, *cleanup_front, *cleanup_back; + const char *script_header; + char error_message[ERROR_BUF_LEN]; }; @@ -71,6 +73,8 @@ ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0); + ctx->script_header = ""; + ctx->error_message[0] = 0; ctx->script_front = ctx->script = malloc(script_len); @@ -235,6 +239,10 @@ return (ctx->inputs[n] == NULL ? "" : ctx->inputs[n]); } +void uw_set_script_header(uw_context ctx, const char *s) { + ctx->script_header = s; +} + static void uw_check_heap(uw_context ctx, size_t extra) { if (ctx->heap_back - ctx->heap_front < extra) { size_t desired = ctx->heap_front - ctx->heap + extra, next; @@ -380,9 +388,9 @@ r[0] = 0; return r; } else { - char *r = uw_malloc(ctx, 41 + (ctx->script_front - ctx->script)); + char *r = uw_malloc(ctx, 41 + (ctx->script_front - ctx->script) + strlen(ctx->script_header)); - sprintf(r, "", ctx->script); + sprintf(r, "%s", ctx->script_header, ctx->script); return r; } } diff -r 4a125bbc602d -r aa2290c32ce2 src/cjr.sml --- a/src/cjr.sml Sun Mar 08 20:34:21 2009 -0400 +++ b/src/cjr.sml Tue Mar 10 10:44:26 2009 -0400 @@ -113,6 +113,10 @@ withtype decl = decl' located -type file = decl list * (Core.export_kind * string * int * typ list * typ) list +datatype sidedness = + ServerOnly + | ServerAndClient + +type file = decl list * (Core.export_kind * string * int * typ list * typ * sidedness) list end diff -r 4a125bbc602d -r aa2290c32ce2 src/cjr_print.sml --- a/src/cjr_print.sml Sun Mar 08 20:34:21 2009 -0400 +++ b/src/cjr_print.sml Tue Mar 10 10:44:26 2009 -0400 @@ -1,4 +1,4 @@ -(* Copyright (c) 2008, Adam Chlipala +(* Copyright (c) 2008-2009, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -2130,7 +2130,7 @@ E.declBinds env d)) env ds - val fields = foldl (fn ((ek, _, _, ts, _), fields) => + val fields = foldl (fn ((ek, _, _, ts, _, _), fields) => case ek of Core.Link => fields | Core.Rpc => fields @@ -2251,7 +2251,7 @@ string "}"] end - fun p_page (ek, s, n, ts, ran) = + fun p_page (ek, s, n, ts, ran, side) = let val (ts, defInputs, inputsVar) = case ek of @@ -2346,6 +2346,12 @@ string "uw_write_header(ctx, \"Content-script-type: text/javascript\\r\\n\");", newline, string "uw_write(ctx, \"\");", + newline, + string "uw_set_script_header(ctx, \"", + string (case side of + ServerAndClient => "\\n" + | ServerOnly => ""), + string "\");", newline]), box [string "{", newline, diff -r 4a125bbc602d -r aa2290c32ce2 src/cjrize.sml --- a/src/cjrize.sml Sun Mar 08 20:34:21 2009 -0400 +++ b/src/cjrize.sml Tue Mar 10 10:44:26 2009 -0400 @@ -519,7 +519,7 @@ val (ts, sm) = ListUtil.foldlMap cifyTyp sm ts val (t, sm) = cifyTyp (t, sm) in - (NONE, SOME (ek, "/" ^ s, n, ts, t), sm) + (NONE, SOME (ek, "/" ^ s, n, ts, t, L'.ServerAndClient), sm) end | L.DTable (s, xts) => diff -r 4a125bbc602d -r aa2290c32ce2 src/compiler.sig --- a/src/compiler.sig Sun Mar 08 20:34:21 2009 -0400 +++ b/src/compiler.sig Tue Mar 10 10:44:26 2009 -0400 @@ -80,6 +80,7 @@ val fuse : (Mono.file, Mono.file) phase val pathcheck : (Mono.file, Mono.file) phase val cjrize : (Mono.file, Cjr.file) phase + val scriptcheck : (Cjr.file, Cjr.file) phase val prepare : (Cjr.file, Cjr.file) phase val sqlify : (Mono.file, Cjr.file) phase @@ -115,6 +116,7 @@ val toMono_shake2 : (string, Mono.file) transform val toPathcheck : (string, Mono.file) transform val toCjrize : (string, Cjr.file) transform + val toScriptcheck : (string, Cjr.file) transform val toPrepare : (string, Cjr.file) transform val toSqlify : (string, Cjr.file) transform diff -r 4a125bbc602d -r aa2290c32ce2 src/compiler.sml --- a/src/compiler.sml Sun Mar 08 20:34:21 2009 -0400 +++ b/src/compiler.sml Tue Mar 10 10:44:26 2009 -0400 @@ -558,12 +558,19 @@ val toCjrize = transform cjrize "cjrize" o toPathcheck +val scriptcheck = { + func = ScriptCheck.classify, + print = CjrPrint.p_file CjrEnv.empty +} + +val toScriptcheck = transform scriptcheck "scriptcheck" o toCjrize + val prepare = { func = Prepare.prepare, print = CjrPrint.p_file CjrEnv.empty } -val toPrepare = transform prepare "prepare" o toCjrize +val toPrepare = transform prepare "prepare" o toScriptcheck val sqlify = { func = Cjrize.cjrize, diff -r 4a125bbc602d -r aa2290c32ce2 src/monoize.sml --- a/src/monoize.sml Sun Mar 08 20:34:21 2009 -0400 +++ b/src/monoize.sml Tue Mar 10 10:44:26 2009 -0400 @@ -1924,9 +1924,7 @@ in case tag of "body" => normal ("body", NONE, - SOME (L'.EStrcat ((L'.EPrim (Prim.String ""), loc), - (L'.EFfiApp ("Basis", "get_script", [(L'.ERecord [], loc)]), - loc)), loc)) + SOME (L'.EFfiApp ("Basis", "get_script", [(L'.ERecord [], loc)]), loc)) | "dyn" => (case attrs of diff -r 4a125bbc602d -r aa2290c32ce2 src/scriptcheck.sig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scriptcheck.sig Tue Mar 10 10:44:26 2009 -0400 @@ -0,0 +1,32 @@ +(* 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. + *) + +signature SCRIPT_CHECK = sig + + val classify : Cjr.file -> Cjr.file + +end diff -r 4a125bbc602d -r aa2290c32ce2 src/scriptcheck.sml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scriptcheck.sml Tue Mar 10 10:44:26 2009 -0400 @@ -0,0 +1,123 @@ +(* 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 ScriptCheck :> SCRIPT_CHECK = struct + +open Cjr + +structure SS = BinarySetFn(struct + type ord_key = string + val compare = String.compare + end) +structure IS = IntBinarySet + +val csBasis = SS.addList (SS.empty, + ["new_client_source", + "get_client_source", + "set_client_source", + "alert"]) + +fun classify (ds, ps) = + let + fun inString {needle, haystack} = + let + val (_, suffix) = Substring.position needle (Substring.full haystack) + in + not (Substring.isEmpty suffix) + end + + fun hasClient csids = + let + fun hasClient e = + case #1 e of + EPrim (Prim.String s) => inString {needle = " false + | ERel _ => false + | ENamed n => IS.member (csids, n) + | ECon (_, _, NONE) => false + | ECon (_, _, SOME e) => hasClient e + | ENone _ => false + | ESome (_, e) => hasClient e + | EFfi ("Basis", x) => SS.member (csBasis, x) + | EFfi _ => false + | EFfiApp ("Basis", x, es) => SS.member (csBasis, x) + orelse List.exists hasClient es + | EFfiApp (_, _, es) => List.exists hasClient es + | EApp (e, es) => hasClient e orelse List.exists hasClient es + | EUnop (_, e) => hasClient e + | EBinop (_, e1, e2) => hasClient e1 orelse hasClient e2 + | ERecord (_, xes) => List.exists (hasClient o #2) xes + | EField (e, _) => hasClient e + | ECase (e, pes, _) => hasClient e orelse List.exists (hasClient o #2) pes + | EError (e, _) => hasClient e + | EWrite e => hasClient e + | ESeq (e1, e2) => hasClient e1 orelse hasClient e2 + | ELet (_, _, e1, e2) => hasClient e1 orelse hasClient e2 + | EQuery {query, body, initial, ...} => hasClient query orelse hasClient body + orelse hasClient initial + | EDml {dml, ...} => hasClient dml + | ENextval {seq, ...} => hasClient seq + | EUnurlify (e, _) => hasClient e + in + hasClient + end + + fun decl ((d, _), csids) = + let + val hasClient = hasClient csids + in + case d of + DVal (_, n, _, e) => if hasClient e then + IS.add (csids, n) + else + csids + | DFun (_, n, _, _, e) => if hasClient e then + IS.add (csids, n) + else + csids + | DFunRec xes => if List.exists (fn (_, _, _, _, e) => hasClient e) xes then + foldl (fn ((_, n, _, _, _), csids) => IS.add (csids, n)) + csids xes + else + csids + | _ => csids + end + + val csids = foldl decl IS.empty ds + + val ps = map (fn (ek, x, n, ts, t, _) => + (ek, x, n, ts, t, + if IS.member (csids, n) then + ServerAndClient + else + ServerOnly)) ps + in + (ds, ps) + end + +end + diff -r 4a125bbc602d -r aa2290c32ce2 src/sources --- a/src/sources Sun Mar 08 20:34:21 2009 -0400 +++ b/src/sources Tue Mar 10 10:44:26 2009 -0400 @@ -160,6 +160,9 @@ cjrize.sig cjrize.sml +scriptcheck.sig +scriptcheck.sml + prepare.sig prepare.sml