changeset 729:7c6b6c3c7b79

Some client-side error handling
author Adam Chlipala <adamc@hcoop.net>
date Thu, 16 Apr 2009 13:47:20 -0400
parents 2197f0e24a9f
children 1b1047992ecf
files lib/js/urweb.js lib/ur/basis.urs src/jscomp.sml src/mono_reduce.sml src/mono_util.sml tests/roundTrip.ur tests/updateErr.ur tests/updateErr.urp
diffstat 8 files changed, 177 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/lib/js/urweb.js	Thu Apr 16 13:00:40 2009 -0400
+++ b/lib/js/urweb.js	Thu Apr 16 13:47:20 2009 -0400
@@ -23,6 +23,79 @@
 }
 
 
+// Error handling
+
+function whine(msg) {
+  alert(msg);
+  throw msg;
+}
+
+function pf() {
+  whine("Pattern match failure");
+}
+
+function runHandlers(ls, arg) {
+  for (; ls; ls = ls.next)
+    try {
+      ls.data(arg)(null);
+    } catch (v) { }
+}
+
+var errorHandlers = null;
+
+function onError(f) {
+  errorHandlers = cons(f, errorHandlers);
+}
+
+function er(s) {
+  runHandlers(errorHandlers, s);
+  throw {uw_error: s};
+}
+
+var failHandlers = null;
+
+function onFail(f) {
+  failHandlers = cons(f, failHandlers);
+}
+
+function doExn(v) {
+  if (v == null || v.uw_error == null) {
+    var s = (v == null ? "null" : v.toString());
+    runHandlers(failHandlers, s);
+  }
+}
+
+var disconnectHandlers = null;
+
+function onDisconnect(f) {
+  disconnectHandlers = cons(function (_){return f}, disconnectHandlers);
+}
+
+function discon() {
+  runHandlers(disconnectHandlers, null);
+}
+
+var connectHandlers = null;
+
+function onConnectFail(f) {
+  connectHandlers = cons(function (_){return f}, connectHandlers);
+}
+
+function conn() {
+  runHandlers(connectHandlers, null);
+}
+
+var serverHandlers = null;
+
+function onServerError(f) {
+  serverHandlers = cons(f, serverHandlers);
+}
+
+function servErr(s) {
+  runHandlers(serverHandlers, s);
+}
+
+
 // Embedding closures in XML strings
 
 function cs(f) {
@@ -90,19 +163,23 @@
 function populate(node) {
   var s = node.signal;
   var oldSources = node.sources;
-  var sr = s();
-  var newSources = sr.sources;
+  try {
+    var sr = s();
+    var newSources = sr.sources;
 
-  for (var sp = oldSources; sp; sp = sp.next)
-    if (!member(sp.data, newSources))
-      sp.data.dyns = remove(node, sp.data.dyns);
+    for (var sp = oldSources; sp; sp = sp.next)
+      if (!member(sp.data, newSources))
+        sp.data.dyns = remove(node, sp.data.dyns);
 
-  for (var sp = newSources; sp; sp = sp.next)
-    if (!member(sp.data, oldSources))
-      sp.data.dyns = cons(node, sp.data.dyns);
+    for (var sp = newSources; sp; sp = sp.next)
+      if (!member(sp.data, oldSources))
+        sp.data.dyns = cons(node, sp.data.dyns);
 
-  node.sources = newSources;
-  node.recreate(sr.data);
+    node.sources = newSources;
+    node.recreate(sr.data);
+  } catch (v) {
+    doExn(v);
+  }
 }
 
 function sc(v) {
@@ -160,7 +237,11 @@
     scriptsCopy[i] = scripts[i];
   for (var i = 0; i < len; ++i) {
     thisScript = scriptsCopy[i];
-    eval(thisScript.textContent);
+    try {
+      eval(thisScript.textContent);
+    } catch (v) {
+      doExn(v);
+    }
   }
 
   thisScript = savedScript;
@@ -227,7 +308,7 @@
   if (r.toString() == s)
     return r;
   else
-    throw "Can't parse int: " + s;
+    er("Can't parse int: " + s);
 }
 
 function pfl(s) {
@@ -235,7 +316,7 @@
   if (r.toString() == s)
     return r;
   else
-    throw "Can't parse float: " + s;
+    er("Can't parse float: " + s);
 }
 
 function uf(s) {
@@ -247,43 +328,6 @@
 }
 
 
-// Error handling
-
-function whine(msg) {
-  alert(msg);
-  throw msg;
-}
-
-function pf() {
-  whine("Pattern match failure");
-}
-
-var errorHandlers = null;
-
-function onError(f) {
-  errorHandlers = cons(f, errorHandlers);
-}
-
-function er(s) {
-  for (var ls = errorHandlers; ls; ls = ls.next)
-    ls.data(s)(null);
-  throw {uw_error: s};
-}
-
-var failHandlers = null;
-
-function onFail(f) {
-  failHandlers = cons(f, failHandlers);
-}
-
-function doExn(v) {
-  if (v == null || v.uw_error == null) {
-    var s = (v == null ? "null" : v.toString());
-    for (var ls = failHandlers; ls; ls = ls.next)
-      ls.data(s)(null);
-  }
-}
-
 
 // Remote calls
 
@@ -333,10 +377,14 @@
           isok = true;
       } catch (e) { }
 
-      if (isok)
-        k(parse(xhr.responseText));
-      else {
-        whine("Error querying remote server!");
+      if (isok) {
+        try {
+          k(parse(xhr.responseText));
+        } catch (v) {
+          doExn(v);
+        }
+      } else {
+        conn();
       }
     }
   };
@@ -406,8 +454,10 @@
 
       if (isok) {
         var lines = xhr.responseText.split("\n");
-        if (lines.length < 2) 
-          return; // throw "Empty message from remote server";
+        if (lines.length < 2) {
+          discon();
+          return;
+        }
 
         for (var i = 0; i+1 < lines.length; i += 2) {
           var chn = lines[i];
@@ -439,9 +489,9 @@
         connect();
       }
       else {
-        /*try {
-          whine("Error querying remote server for messages! " + xhr.status);
-        } catch (e) { }*/
+        try {
+          servError("Error querying remote server for messages: " + xhr.status);
+        } catch (e) { servError("Error querying remote server for messages"); }
       }
     }
   };
--- a/lib/ur/basis.urs	Thu Apr 16 13:00:40 2009 -0400
+++ b/lib/ur/basis.urs	Thu Apr 16 13:47:20 2009 -0400
@@ -552,8 +552,11 @@
 
 val error : t ::: Type -> xbody -> t
 
+(* Client-side-only handlers: *)
 val onError : (xbody -> transaction unit) -> transaction unit
 val onFail : (string -> transaction unit) -> transaction unit
-(* Client-side only *)
+val onConnectFail : transaction unit -> transaction unit
+val onDisconnect : transaction unit -> transaction unit
+val onServerError : (string -> transaction unit) -> transaction unit
 
 val show_xml : ctx ::: {Unit} -> use ::: {Type} -> bind ::: {Type} -> show (xml ctx use bind)
--- a/src/jscomp.sml	Thu Apr 16 13:00:40 2009 -0400
+++ b/src/jscomp.sml	Thu Apr 16 13:47:20 2009 -0400
@@ -53,7 +53,11 @@
              (("Basis", "strcat"), "cat"),
              (("Basis", "intToString"), "ts"),
              (("Basis", "floatToString"), "ts"),
-             (("Basis", "onError"), "onError")]
+             (("Basis", "onError"), "onError"),
+             (("Basis", "onFail"), "onFail"),
+             (("Basis", "onConnectFail"), "onConnectFail"),
+             (("Basis", "onDisconnect"), "onDisconnect"),
+             (("Basis", "onServerError"), "onServerError")]
 
 structure FM = BinaryMapFn(struct
                            type ord_key = string * string
@@ -764,6 +768,11 @@
                             end
                           | EBinop (s, e1, e2) =>
                             let
+                                val s =
+                                    case s of
+                                        "!strcmp" => "=="
+                                      | _ => s
+
                                 val (e1, st) = jsE inner (e1, st)
                                 val (e2, st) = jsE inner (e2, st)
                             in
--- a/src/mono_reduce.sml	Thu Apr 16 13:00:40 2009 -0400
+++ b/src/mono_reduce.sml	Thu Apr 16 13:47:20 2009 -0400
@@ -62,6 +62,10 @@
       | EFfiApp ("Basis", "subscribe", _) => true
       | EFfiApp ("Basis", "send", _) => true
       | EFfiApp ("Basis", "onError", _) => true
+      | EFfiApp ("Basis", "onFail", _) => true
+      | EFfiApp ("Basis", "onConnectFail", _) => true
+      | EFfiApp ("Basis", "onDisconnect", _) => true
+      | EFfiApp ("Basis", "onServerError", _) => true
       | EFfiApp _ => false
       | EApp ((EFfi _, _), _) => false
       | EApp _ => true
@@ -287,6 +291,10 @@
                       | EFfiApp ("Basis", "subscribe", es) => ffi es
                       | EFfiApp ("Basis", "send", es) => ffi es
                       | EFfiApp ("Basis", "onError", es) => ffi es
+                      | EFfiApp ("Basis", "onFail", es) => ffi es
+                      | EFfiApp ("Basis", "onConnectFail", es) => ffi es
+                      | EFfiApp ("Basis", "onDisconnect", es) => ffi es
+                      | EFfiApp ("Basis", "onServerError", es) => ffi es
                       | EFfiApp (_, _, es) => List.concat (map (summarize d) es)
                       | EApp ((EFfi _, _), e) => summarize d e
                       | EApp _ =>
--- a/src/mono_util.sml	Thu Apr 16 13:00:40 2009 -0400
+++ b/src/mono_util.sml	Thu Apr 16 13:47:20 2009 -0400
@@ -325,15 +325,19 @@
                                 fn t' =>
                                    (EUnurlify (e', t'), loc)))
               | EJavaScript (m, e, NONE) =>
-                S.map2 (mfe ctx e,
-                     fn e' =>
-                        (EJavaScript (m, e', NONE), loc))
+                S.bind2 (mfmode ctx m,
+                         fn m' =>
+                            S.map2 (mfe ctx e,
+                                 fn e' =>
+                                    (EJavaScript (m', e', NONE), loc)))
               | EJavaScript (m, e, SOME e2) =>
-                S.bind2 (mfe ctx e,
-                     fn e' =>
-                        S.map2 (mfe ctx e2,
-                             fn e2' =>
-                                (EJavaScript (m, e', SOME e2'), loc)))
+                S.bind2 (mfmode ctx m,
+                         fn m' =>
+                            S.bind2 (mfe ctx e,
+                                  fn e' =>
+                                     S.map2 (mfe ctx e2,
+                                          fn e2' =>
+                                             (EJavaScript (m, e', SOME e2'), loc))))
 
               | ESignalReturn e =>
                 S.map2 (mfe ctx e,
@@ -372,6 +376,14 @@
                          S.map2 (mfe ctx ek,
                                fn ek' =>
                                   (ESleep (s', ek'), loc)))
+
+        and mfmode ctx mode =
+            case mode of
+                Attribute => S.return2 mode
+              | Script => S.return2 mode
+              | Source t =>
+                S.map2 (mft t,
+                     fn t' => Source t')
     in
         mfe
     end
--- a/tests/roundTrip.ur	Thu Apr 16 13:00:40 2009 -0400
+++ b/tests/roundTrip.ur	Thu Apr 16 13:47:20 2009 -0400
@@ -26,11 +26,14 @@
             receiverB ()
 
         fun sender s n f =
-            sleep 9;
+            sleep 2000;
             writeBack (s, n, f);
             sender (s ^ "!") (n + 1) (f + 1.23)
     in
-        return <xml><body onload={spawn (receiverA ()); spawn (receiverB ()); sender "" 0 0.0}>
+        return <xml><body onload={onDisconnect (alert "Server booted me");
+                                  onConnectFail (alert "Connection failed");
+                                  onServerError (fn s => alert ("Server error: " ^ s));
+                                  spawn (receiverA ()); spawn (receiverB ()); sender "" 0 0.0}>
           <dyn signal={Buffer.render buf}/>
         </body></xml>
     end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/updateErr.ur	Thu Apr 16 13:47:20 2009 -0400
@@ -0,0 +1,17 @@
+fun main () : transaction page =
+    s <- source "";
+    b <- Buffer.create;
+    txt <- source "";
+
+    return <xml><body onload={onError (fn xml => Buffer.write b (show xml));
+                              onFail (fn s => alert ("FAIL! " ^ s))}>
+      <dyn signal={s <- signal s; return <xml>{[s]}</xml>}/><br/>
+      <dyn signal={s <- signal s; if s = "" then return <xml>Init</xml> else error <xml>Crapky</xml>}/><br/>
+      <dyn signal={s <- signal s; return <xml>"{[s]}"</xml>}/><br/>
+
+      <ctextbox source={txt}/> <button onclick={s' <- get txt; set s s'; set txt ""}/>
+
+      <hr/>
+
+      <dyn signal={Buffer.render b}/>
+    </body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/updateErr.urp	Thu Apr 16 13:47:20 2009 -0400
@@ -0,0 +1,4 @@
+debug
+
+buffer
+updateErr