← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1543001-eris into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-1543001-eris into lp:widelands.

Commit message:
Updated Eris to version 1.1.0.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1543001 in widelands: "Update Eris for build 19"
  https://bugs.launchpad.net/widelands/+bug/1543001

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1543001-eris/+merge/291294

I just dumped the files and updated the readme. As long as we don't change to a newer Lua version, savegames should be compatible. I did a quick test with my long Trident of Fire savegame, and it loaded with no issues.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1543001-eris into lp:widelands.
=== modified file 'src/third_party/eris/README.eris'
--- src/third_party/eris/README.eris	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/README.eris	2016-04-07 18:16:25 +0000
@@ -1,11 +1,11 @@
 This directory contains a verbatim copy of Eris by Florian Nücke.
 
 URL: https://github.com/fnuecke/eris
-VERSION: 2e39ecc7dcb73120dde775929227fa661fbc6bc0
+VERSION: 1.1.0 for Lua 5.2.4
 
 We use this for heavy persistence and it also brings with it the Lua version
 that we use in Widelands. The Widelands Team wishes to expresses total and
 complete gratitude to the authors of Eris for making it available under the MIT
-License. 
+License.
 
    -- SirVer, in behalf of the Widelands Team

=== modified file 'src/third_party/eris/eris.c'
--- src/third_party/eris/eris.c	2014-07-25 11:14:03 +0000
+++ src/third_party/eris/eris.c	2016-04-07 18:16:25 +0000
@@ -1,6 +1,6 @@
 /*
-Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
-Copyright (c) 2013 by Florian Nuecke.
+Eris - Heavy-duty persistence for Lua 5.2.4 - Based on Pluto
+Copyright (c) 2013-2015 by Florian Nuecke.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -121,6 +121,7 @@
 #define eris_reallocvector luaM_reallocvector
 /* lobject.h */
 #define eris_ttypenv ttypenv
+#define eris_clLvalue clLvalue
 #define eris_setnilvalue setnilvalue
 #define eris_setclLvalue setclLvalue
 #define eris_setobj setobj
@@ -176,6 +177,47 @@
 
 /*
 ** ============================================================================
+** Language strings for errors.
+** ============================================================================
+*/
+
+#define ERIS_ERR_CFUNC "attempt to persist a light C function (%p)"
+#define ERIS_ERR_COMPLEXITY "object too complex"
+#define ERIS_ERR_HOOK "cannot persist yielded hooks"
+#define ERIS_ERR_METATABLE "bad metatable, not nil or table"
+#define ERIS_ERR_NOFUNC "attempt to persist unknown function type"
+#define ERIS_ERR_READ "could not read data"
+#define ERIS_ERR_SPER_FUNC "%s did not return a function"
+#define ERIS_ERR_SPER_LOAD "bad unpersist function (%s expected, returned %s)"
+#define ERIS_ERR_SPER_PROT "attempt to persist forbidden table"
+#define ERIS_ERR_SPER_TYPE "%d not nil, boolean, or function"
+#define ERIS_ERR_SPER_UFUNC "invalid restore function"
+#define ERIS_ERR_SPER_UPERM "bad permanent value (%s expected, got %s)"
+#define ERIS_ERR_SPER_UPERMNIL "bad permanent value (no value)"
+#define ERIS_ERR_STACKBOUNDS "stack index out of bounds"
+#define ERIS_ERR_TABLE "bad table value, got a nil value"
+#define ERIS_ERR_THREAD "cannot persist currently running thread"
+#define ERIS_ERR_THREADCI "invalid callinfo"
+#define ERIS_ERR_THREADCTX "bad C continuation function"
+#define ERIS_ERR_THREADERRF "invalid errfunc"
+#define ERIS_ERR_THREADPC "saved program counter out of bounds"
+#define ERIS_ERR_TRUNC_INT "int value would get truncated"
+#define ERIS_ERR_TRUNC_SIZE "size_t value would get truncated"
+#define ERIS_ERR_TYPE_FLOAT "unsupported lua_Number type"
+#define ERIS_ERR_TYPE_INT "unsupported int type"
+#define ERIS_ERR_TYPE_SIZE "unsupported size_t type"
+#define ERIS_ERR_TYPEP "trying to persist unknown type %d"
+#define ERIS_ERR_TYPEU "trying to unpersist unknown type %d"
+#define ERIS_ERR_UCFUNC "bad C closure (C function expected, got %s)"
+#define ERIS_ERR_UCFUNCNULL "bad C closure (C function expected, got null)"
+#define ERIS_ERR_USERDATA "attempt to literally persist userdata"
+#define ERIS_ERR_WRITE "could not write data"
+#define ERIS_ERR_REF "invalid reference #%d. this usually means a special "\
+                      "persistence callback of a table referenced said table "\
+                      "(directly or indirectly via an upvalue)."
+
+/*
+** ============================================================================
 ** Constants, settings, types and forward declarations.
 ** ============================================================================
 */
@@ -266,6 +308,12 @@
 #define BUFFIDX 3
 #define PATHIDX 4
 
+/* Table indices for upvalue tables, keeping track of upvals to open. */
+#define UVTOCL 1
+#define UVTONU 2
+#define UVTVAL 3
+#define UVTREF 4
+
 /* }======================================================================== */
 
 /*
@@ -405,12 +453,13 @@
 }
 
 /* Used as a callback for luaL_opt to check boolean setting values. */
-static void
+static bool
 checkboolean(lua_State *L, int narg) {                       /* ... bool? ... */
   if (!lua_isboolean(L, narg)) {                                /* ... :( ... */
-    luaL_argerror(L, narg, lua_pushfstring(L,
+    return luaL_argerror(L, narg, lua_pushfstring(L,
       "boolean expected, got %s", lua_typename(L, lua_type(L, narg))));
   }                                                           /* ... bool ... */
+  return lua_toboolean(L, narg);
 }
 
 /* }======================================================================== */
@@ -427,7 +476,7 @@
 /* Writes a raw memory block with the specified size. */
 #define WRITE_RAW(value, size) {\
   if (info->u.pi.writer(info->L, (value), (size), info->u.pi.ud)) \
-    eris_error(info, "could not write data"); }
+    eris_error(info, ERIS_ERR_WRITE); }
 
 /* Writes a single value with the specified type. */
 #define WRITE_VALUE(value, type) write_##type(info, value)
@@ -441,7 +490,7 @@
 /* Reads a raw block of memory with the specified size. */
 #define READ_RAW(value, size) {\
   if (eris_read(&info->u.upi.zio, (value), (size))) \
-    eris_error(info, "could not read data"); }
+    eris_error(info, ERIS_ERR_READ); }
 
 /* Reads a single value with the specified type. */
 #define READ_VALUE(type) read_##type(info)
@@ -527,7 +576,7 @@
     write_int64_t(info, value);
   }
   else {
-    eris_error(info, "unsupported int type");
+    eris_error(info, ERIS_ERR_TYPE_INT);
   }
 }
 
@@ -543,7 +592,7 @@
     write_uint64_t(info, value);
   }
   else {
-    eris_error(info, "unsupported size_t type");
+    eris_error(info, ERIS_ERR_TYPE_SIZE);
   }
 }
 
@@ -556,7 +605,7 @@
     write_float64(info, value);
   }
   else {
-    eris_error(info, "unsupported lua_Number type");
+    eris_error(info, ERIS_ERR_TYPE_FLOAT);
   }
 }
 
@@ -657,25 +706,25 @@
     int16_t pvalue = read_int16_t(info);
     value = (int)pvalue;
     if ((int32_t)value != pvalue) {
-      eris_error(info, "int value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_INT);
     }
   }
   else if (info->u.upi.sizeof_int == sizeof(int32_t)) {
     int32_t pvalue = read_int32_t(info);
     value = (int)pvalue;
     if ((int32_t)value != pvalue) {
-      eris_error(info, "int value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_INT);
     }
   }
   else if (info->u.upi.sizeof_int == sizeof(int64_t)) {
     int64_t pvalue = read_int64_t(info);
     value = (int)pvalue;
     if ((int64_t)value != pvalue) {
-      eris_error(info, "int value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_INT);
     }
   }
   else {
-    eris_error(info, "unsupported int type");
+    eris_error(info, ERIS_ERR_TYPE_INT);
     value = 0; /* not reached */
   }
   return value;
@@ -688,25 +737,25 @@
     uint16_t pvalue = read_uint16_t(info);
     value = (size_t)pvalue;
     if ((uint32_t)value != pvalue) {
-      eris_error(info, "size_t value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_SIZE);
     }
   }
   else if (info->u.upi.sizeof_size_t == sizeof(uint32_t)) {
     uint32_t pvalue = read_uint32_t(info);
     value = (size_t)pvalue;
     if ((uint32_t)value != pvalue) {
-      eris_error(info, "size_t value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_SIZE);
     }
   }
   else if (info->u.upi.sizeof_size_t == sizeof(uint64_t)) {
     uint64_t pvalue = read_uint64_t(info);
     value = (size_t)pvalue;
     if ((uint64_t)value != pvalue) {
-      eris_error(info, "size_t value would get truncated");
+      eris_error(info, ERIS_ERR_TRUNC_SIZE);
     }
   }
   else {
-    eris_error(info, "unsupported size_t type");
+    eris_error(info, ERIS_ERR_TYPE_SIZE);
     value = 0; /* not reached */
   }
   return value;
@@ -721,7 +770,7 @@
     return read_float64(info);
   }
   else {
-    eris_error(info, "unsupported lua_Number type");
+    eris_error(info, ERIS_ERR_TYPE_FLOAT);
     return 0; /* not reached */
   }
 }
@@ -801,7 +850,7 @@
 u_string(Info *info) {                                                 /* ... */
   eris_checkstack(info->L, 2);
   {
-	 // TODO(unknown): Can we avoid this copy somehow? (Without it getting too nasty)
+    /* TODO Can we avoid this copy somehow? (Without it getting too nasty) */
     const size_t length = READ_VALUE(size_t);
     char *value = lua_newuserdata(info->L, length * sizeof(char)); /* ... tmp */
     READ_RAW(value, length);
@@ -843,7 +892,7 @@
     lua_pop(info->L, 1);                                           /* ... tbl */
   }
   else {                                                            /* tbl :( */
-    eris_error(info, "bad metatable, not nil or table");
+    eris_error(info, ERIS_ERR_METATABLE);
   }
   poppath(info);
 }
@@ -923,7 +972,7 @@
       lua_rawset(info->L, -3);                                     /* ... tbl */
     }
     else {
-      eris_error(info, "bad table value, got a nil value");
+      eris_error(info, ERIS_ERR_TABLE);
     }
 
     poppath(info);
@@ -996,8 +1045,7 @@
           lua_call(info->L, 1, 1);                           /* ... obj func? */
         }
         if (!lua_isfunction(info->L, -1)) {                     /* ... obj :( */
-          eris_error(info, "%s did not return a function",
-                     info->u.pi.metafield);
+          eris_error(info, ERIS_ERR_SPER_FUNC, info->u.pi.metafield);
         }                                                     /* ... obj func */
 
         /* Special persistence, call this function when unpersisting. */
@@ -1006,8 +1054,7 @@
         lua_pop(info->L, 1);                                       /* ... obj */
         return;
       default:                                               /* ... obj mt :( */
-        eris_error(info, "%d not nil, boolean, or function",
-                   info->u.pi.metafield);
+        eris_error(info, ERIS_ERR_SPER_TYPE, info->u.pi.metafield);
         return; /* not reached */
     }
   }
@@ -1018,10 +1065,10 @@
     literal(info);                                                 /* ... obj */
   }
   else if (lua_type(info->L, -1) == LUA_TTABLE) {
-    eris_error(info, "attempt to persist forbidden table");
+    eris_error(info, ERIS_ERR_SPER_PROT);
   }
   else {
-    eris_error(info, "attempt to literally persist userdata");
+    eris_error(info, ERIS_ERR_USERDATA);
   }
 }
 
@@ -1040,7 +1087,7 @@
      * persisting a special object. */
     unpersist(info);                                           /* ... spfunc? */
     if (!lua_isfunction(info->L, -1)) {                             /* ... :( */
-      eris_error(info, "invalid restore function");
+      eris_error(info, ERIS_ERR_SPER_UFUNC);
     }                                                           /* ... spfunc */
 
     if (info->passIOToPersist) {
@@ -1051,8 +1098,9 @@
     }
 
     if (lua_type(info->L, -1) != type) {                            /* ... :( */
-      eris_error(info, "bad unpersist function (%s expected, returned %s)",
-                       kTypenames[type], kTypenames[lua_type(info->L, -1)]);
+      const char *want = kTypenames[type];
+      const char *have = kTypenames[lua_type(info->L, -1)];
+      eris_error(info, ERIS_ERR_SPER_LOAD, want, have);
     }                                                              /* ... obj */
 
     /* Update the reftable entry. */
@@ -1326,12 +1374,14 @@
 u_upval(Info *info) {                                                  /* ... */
   eris_checkstack(info->L, 2);
 
-  /* Create the table we use to store the pointer to the actual upval (1), the
-   * value of the upval (2) and any pointers to the pointer to the upval (3+).*/
-  lua_createtable(info->L, 3, 0);                                  /* ... tbl */
+  /* Create the table we use to store the stack location to the upval (1+2),
+   * the value of the upval (3) and any references to the upvalue's value (4+).
+   * References are stored as two entries each, the actual closure holding the
+   * upvalue, and the index of the upvalue in that closure. */
+  lua_createtable(info->L, 5, 0);                                  /* ... tbl */
   registerobject(info);
   unpersist(info);                                             /* ... tbl obj */
-  lua_rawseti(info->L, -2, 1);                                     /* ... tbl */
+  lua_rawseti(info->L, -2, UVTVAL);                                /* ... tbl */
 
   eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
 }
@@ -1354,8 +1404,7 @@
   switch (ttype(info->L->top - 1)) {
     case LUA_TLCF: /* light C function */
       /* We cannot persist these, they have to be handled via the permtable. */
-      eris_error(info, "attempt to persist a light C function (%p)",
-                 lua_tocfunction(info->L, -1));
+      eris_error(info, ERIS_ERR_CFUNC, lua_tocfunction(info->L, -1));
       return; /* not reached */
     case LUA_TCCL: /* C closure */ {                  /* perms reftbl ... ccl */
       CClosure *cl = clCvalue(info->L->top - 1);
@@ -1387,7 +1436,7 @@
       break;
     }
     case LUA_TLCL: /* Lua function */ {               /* perms reftbl ... lcl */
-      LClosure *cl = clLvalue(info->L->top - 1);
+      LClosure *cl = eris_clLvalue(info->L->top - 1);
       /* Mark it as a Lua closure. */
       WRITE_VALUE(false, uint8_t);
       /* Write the upvalue count first, since we have to know it when creating
@@ -1421,7 +1470,7 @@
       break;
     }
     default:
-      eris_error(info, "attempt to persist unknown function type");
+      eris_error(info, ERIS_ERR_NOFUNC);
       return; /* not reached */
   }
 }
@@ -1444,12 +1493,11 @@
     /* Read the C function from the permanents table. */
     unpersist(info);                                             /* ... cfunc */
     if (!lua_iscfunction(info->L, -1)) {
-      eris_error(info, "bad C closure (C function expected, got %s)",
-                 kTypenames[lua_type(info->L, -1)]);
+      eris_error(info, ERIS_ERR_UCFUNC, kTypenames[lua_type(info->L, -1)]);
     }
     f = lua_tocfunction(info->L, -1);
     if (!f) {
-      eris_error(info, "bad C closure (C function expected, got null)");
+      eris_error(info, ERIS_ERR_UCFUNCNULL);
     }
     lua_pop(info->L, 1);                                               /* ... */
 
@@ -1524,50 +1572,64 @@
       }
       unpersist(info);                                         /* ... lcl tbl */
       eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
-      lua_rawgeti(info->L, -1, 2);                   /* ... lcl tbl upval/nil */
+      lua_rawgeti(info->L, -1, UVTOCL);               /* ... lcl tbl olcl/nil */
       if (lua_isnil(info->L, -1)) {                        /* ... lcl tbl nil */
         lua_pop(info->L, 1);                                   /* ... lcl tbl */
+        lua_pushvalue(info->L, -2);                        /* ... lcl tbl lcl */
+        lua_rawseti(info->L, -2, UVTOCL);                      /* ... lcl tbl */
+        lua_pushinteger(info->L, nup);                     /* ... lcl tbl nup */
+        lua_rawseti(info->L, -2, UVTONU);                      /* ... lcl tbl */
         *uv = eris_newupval(info->L);
-        lua_pushlightuserdata(info->L, *uv);             /* ... lcl tbl upval */
-        lua_rawseti(info->L, -2, 2);                           /* ... lcl tbl */
-      }
-      else {                                             /* ... lcl tbl upval */
-        eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
-        *uv = (UpVal*)lua_touserdata(info->L, -1);
-        lua_pop(info->L, 1);                                   /* ... lcl tbl */
-      }
+      }
+      else {                                              /* ... lcl tbl olcl */
+        LClosure *ocl;
+        int onup;
+        eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
+        ocl = eris_clLvalue(info->L->top - 1);
+        lua_pop(info->L, 1);                                   /* ... lcl tbl */
+        lua_rawgeti(info->L, -1, UVTONU);                 /* ... lcl tbl onup */
+        eris_assert(lua_type(info->L, -1) == LUA_TNUMBER);
+        onup = lua_tointeger(info->L, -1);
+        lua_pop(info->L, 1);                                   /* ... lcl tbl */
+        *uv = ocl->upvals[onup - 1];
+      }
+      luaC_objbarrier(info->L, cl, *uv);
 
       /* Set the upvalue's actual value and add our reference to the upvalue to
-       * the list, for pointer patching if we have to open the upvalue in
+       * the list, for reference patching if we have to open the upvalue in
        * u_thread. Either is only necessary if the upvalue is still closed. */
       if ((*uv)->v == &(*uv)->u.value) {
+        int i;
         /* Always update the value of the upvalue's value for closed upvalues,
          * even if we re-used one - if we had a cycle, it might have been
-         * incorrectly initialized to nil before. */
-        lua_rawgeti(info->L, -1, 1);                       /* ... lcl tbl obj */
+         * incorrectly initialized to nil before (or rather, not yet set). */
+        lua_rawgeti(info->L, -1, UVTVAL);                  /* ... lcl tbl obj */
         eris_setobj(info->L, &(*uv)->u.value, info->L->top - 1);
         lua_pop(info->L, 1);                                   /* ... lcl tbl */
 
-        lua_pushlightuserdata(info->L, uv);             /* ... lcl tbl upvalp */
-        if (luaL_len(info->L, -2) >= 2) {
-          /* Got a valid sequence, insert at the end. */
-          lua_rawseti(info->L, -2, luaL_len(info->L, -2) + 1); /* ... lcl tbl */
+        lua_pushinteger(info->L, nup);                     /* ... lcl tbl nup */
+        lua_pushvalue(info->L, -3);                    /* ... lcl tbl nup lcl */
+        if (luaL_len(info->L, -3) >= UVTVAL) {
+          /* Got a valid sequence (value already set), insert at the end. */
+          i = luaL_len(info->L, -3);
+          lua_rawseti(info->L, -3, i + 1);                 /* ... lcl tbl nup */
+          lua_rawseti(info->L, -2, i + 2);                     /* ... lcl tbl */
         }
-        else {                                          /* ... lcl tbl upvalp */
-          int i;
+        else {                                         /* ... lcl tbl nup lcl */
           /* Find where to insert. This can happen if we have cycles, in which
            * case the table is not fully initialized at this point, i.e. the
            * value is not in it, yet (we work around that by always setting it,
            * as seen above). */
-          for (i = 3;; ++i) {
-            lua_rawgeti(info->L, -2, i);     /* ... lcl tbl upvalp upvalp/nil */
-            if (lua_isnil(info->L, -1)) {           /* ... lcl tbl upvalp nil */
-              lua_pop(info->L, 1);                      /* ... lcl tbl upvalp */
-              lua_rawseti(info->L, -2, i);                     /* ... lcl tbl */
+          for (i = UVTREF;; i += 2) {
+            lua_rawgeti(info->L, -3, i);       /* ... lcl tbl nup lcl lcl/nil */
+            if (lua_isnil(info->L, -1)) {          /* ... lcl tbl nup lcl nil */
+              lua_pop(info->L, 1);                     /* ... lcl tbl nup lcl */
+              lua_rawseti(info->L, -3, i);                 /* ... lcl tbl nup */
+              lua_rawseti(info->L, -2, i + 1);                 /* ... lcl tbl */
               break;
             }
             else {
-              lua_pop(info->L, 1);                      /* ... lcl tbl upvalp */
+              lua_pop(info->L, 1);                     /* ... lcl tbl nup lcl */
             }
           }                                                    /* ... lcl tbl */
         }
@@ -1590,23 +1652,22 @@
 static void
 p_thread(Info *info) {                                          /* ... thread */
   lua_State* thread = lua_tothread(info->L, -1);
-  size_t level;
-  StkId o;
+  size_t level = 0, total = thread->top - thread->stack;
   CallInfo *ci;
-  UpVal *uv;
+  GCObject *uvi;
 
   eris_checkstack(info->L, 2);
 
   /* We cannot persist any running threads, because by definition we *are* that
    * running thread. And we use the stack. So yeah, really not a good idea. */
   if (thread == info->L) {
-    eris_error(info, "cannot persist currently running thread");
+    eris_error(info, ERIS_ERR_THREAD);
     return; /* not reached */
   }
 
   /* Persist the stack. Save the total size and used space first. */
   WRITE_VALUE(thread->stacksize, int);
-  WRITE_VALUE(thread->top - thread->stack, size_t);
+  WRITE_VALUE(total, size_t);
 
   /* The Lua stack looks like this:
    * stack ... top ... stack_last
@@ -1614,10 +1675,15 @@
    * element, i.e. there's nothing stored there. So we stop one below that. */
   pushpath(info, ".stack");
   lua_pushnil(info->L);                                     /* ... thread nil */
-  level = 0;
-  for (o = thread->stack; o < thread->top; ++o) {
-    pushpath(info, "[%d]", level++);
-    eris_setobj(info->L, info->L->top - 1, o);              /* ... thread obj */
+  /* Since the thread's stack may be re-allocated in the meantime, we cannot
+   * use pointer arithmetic here (i.e. o = thread->stack; ...; ++o). Instead we
+   * have to keep track of our position in the stack directly (which we do for
+   * the path info anyway) and compute the actual address each time.
+   */
+  for (; level < total; ++level) {
+    pushpath(info, "[%d]", level);
+    eris_setobj(info->L, info->L->top - 1, thread->stack + level);
+                                                            /* ... thread obj */
     persist(info);                                          /* ... thread obj */
     poppath(info);
   }
@@ -1647,7 +1713,7 @@
   WRITE_VALUE(thread->hookcount, int); */
 
   if (thread->hook) {
-	 // TODO(unknown): Warn that hooks are not persisted?
+    /* TODO Warn that hooks are not persisted? */
   }
 
   /* Write call information (stack frames). In 5.2 CallInfo is stored in a
@@ -1679,7 +1745,7 @@
 
     eris_assert(eris_isLua(ci) || (ci->callstatus & CIST_TAIL) == 0);
     if (ci->callstatus & CIST_HOOKYIELD) {
-      eris_error(info, "cannot persist yielded hooks");
+      eris_error(info, ERIS_ERR_HOOK);
     }
 
     if (eris_isLua(ci)) {
@@ -1694,7 +1760,7 @@
       WRITE_VALUE(ci->u.c.old_errfunc, ptrdiff_t);
       WRITE_VALUE(ci->u.c.old_allowhook, uint8_t); */
 
-		// TODO(unknown): Is this really right? Hooks may be a problem?
+      /* TODO Is this really right? Hooks may be a problem? */
       if (ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
         WRITE_VALUE(ci->u.c.ctx, int);
         eris_assert(ci->u.c.k);
@@ -1719,11 +1785,12 @@
   pushpath(info, ".openupval");
   lua_pushnil(info->L);                                     /* ... thread nil */
   level = 0;
-  for (uv = eris_gco2uv(thread->openupval);
-       uv != NULL;
-       uv = eris_gco2uv(eris_gch(eris_obj2gco(uv))->next))
+  for (uvi = thread->openupval;
+       uvi != NULL;
+       uvi = eris_gch(uvi)->next)
   {
-    pushpath(info, "[%d]", level);
+    UpVal *uv = eris_gco2uv(uvi);
+    pushpath(info, "[%d]", level++);
     WRITE_VALUE(eris_savestackidx(thread, uv->v) + 1, size_t);
     eris_setobj(info->L, info->L->top - 1, uv->v);          /* ... thread obj */
     lua_pushlightuserdata(info->L, uv);                  /* ... thread obj id */
@@ -1739,7 +1806,7 @@
 #define validate(stackpos, inclmax) \
   if ((stackpos) < thread->stack || stackpos > (inclmax)) { \
     (stackpos) = thread->stack; \
-    eris_error(info, "stack index out of bounds"); }
+    eris_error(info, ERIS_ERR_STACKBOUNDS); }
 
 /* I had so hoped to get by without any 'hacks', but I surrender. We mark the
  * thread as incomplete to avoid the GC messing with it while we're building
@@ -1806,7 +1873,7 @@
     o = eris_restorestack(thread, thread->errfunc);
     validate(o, thread->top);
     if (eris_ttypenv(o) != LUA_TFUNCTION) {
-      eris_error(info, "invalid errfunc");
+      eris_error(info, ERIS_ERR_THREADERRF);
     }
   }
   /* These are only used while a thread is being executed or can be deduced:
@@ -1842,7 +1909,7 @@
       o = eris_restorestack(thread, thread->ci->extra);
       validate(o, thread->top);
       if (eris_ttypenv(o) != LUA_TFUNCTION) {
-        eris_error(info, "invalid callinfo");
+        eris_error(info, ERIS_ERR_THREADCI);
       }
     }
 
@@ -1855,7 +1922,7 @@
           thread->ci->u.l.savedpc > lcl->p->code + lcl->p->sizecode)
       {
         thread->ci->u.l.savedpc = lcl->p->code; /* Just to be safe. */
-        eris_error(info, "saved program counter out of bounds");
+        eris_error(info, ERIS_ERR_THREADPC);
       }
     }
     else {
@@ -1867,7 +1934,7 @@
       thread->ci->u.c.old_errfunc = 0;
       thread->ci->u.c.old_allowhook = 0;
 
-		// TODO(unknown): Is this really right?
+      /* TODO Is this really right? */
       if (thread->ci->callstatus & (CIST_YPCALL | CIST_YIELDED)) {
         thread->ci->u.c.ctx = READ_VALUE(int);
         LOCK(thread);
@@ -1877,7 +1944,7 @@
           thread->ci->u.c.k = lua_tocfunction(info->L, -1);
         }
         else {
-          eris_error(info, "bad C continuation function");
+          eris_error(info, ERIS_ERR_THREADCTX);
           return; /* not reached */
         }
         lua_pop(info->L, 1);                                    /* ... thread */
@@ -1905,7 +1972,7 @@
     o = eris_restorestack(thread, thread->ci->extra);
     validate(o, thread->top);
     if (eris_ttypenv(o) != LUA_TFUNCTION) {
-      eris_error(info, "invalid callinfo");
+      eris_error(info, ERIS_ERR_THREADCI);
     }
   }
   LOCK(thread);
@@ -1919,7 +1986,7 @@
    * functions using them having been unpersisted (they'll usually be in the
    * stack of the thread). For this reason we store all previous references to
    * the upvalue in a table that is returned when we try to unpersist an
-   * upvalue, so that we can adjust these pointers in here. */
+   * upvalue, so that we can adjust these references in here. */
   LOCK(thread);
   pushpath(info, ".openupval");
   UNLOCK(thread);
@@ -1948,19 +2015,27 @@
     nuv = eris_findupval(thread, stk);
     UNLOCK(thread);
 
-    /* Then check if we need to patch some pointers. */
-    lua_rawgeti(info->L, -1, 2);                  /* ... thread tbl upval/nil */
-    if (!lua_isnil(info->L, -1)) {                    /* ... thread tbl upval */
+    /* Then check if we need to patch some references. */
+    lua_rawgeti(info->L, -1, UVTREF);               /* ... thread tbl lcl/nil */
+    if (!lua_isnil(info->L, -1)) {                      /* ... thread tbl lcl */
       int i, n;
-      eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
+      eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
       /* Already exists, replace it. To do this we have to patch all the
-       * pointers to the already existing one, which we added to the table in
-       * u_closure, starting at index 3. */
+       * references to the already existing one, which we added to the table in
+       * u_closure. */
       lua_pop(info->L, 1);                                  /* ... thread tbl */
-      for (i = 3, n = luaL_len(info->L, -1); i <= n; ++i) {
-        lua_rawgeti(info->L, -1, i);                 /* ... thread tbl upvalp */
-        (*(UpVal**)lua_touserdata(info->L, -1)) = nuv;
-        lua_pop(info->L, 1);                                /* ... thread tbl */
+      for (i = UVTREF, n = luaL_len(info->L, -1); i <= n; i += 2) {
+        LClosure *cl;
+        int nup;
+        lua_rawgeti(info->L, -1, i);                    /* ... thread tbl lcl */
+        cl = eris_clLvalue(info->L->top - 1);
+        lua_pop(info->L, 1);                                /* ... thread tbl */
+        lua_rawgeti(info->L, -1, i + 1);                /* ... thread tbl nup */
+        nup = lua_tointeger(info->L, -1);
+        lua_pop(info->L, 1);                                /* ... thread tbl */
+        /* Open the upvalue by pointing to the stack and register in GC. */
+        cl->upvals[nup - 1] = nuv;
+        luaC_objbarrier(info->L, cl, nuv);
       }
     }
     else {                                              /* ... thread tbl nil */
@@ -1970,8 +2045,6 @@
 
     /* Store open upvalue in table for future references. */
     LOCK(thread);
-    lua_pushlightuserdata(info->L, nuv);              /* ... thread tbl upval */
-    lua_rawseti(info->L, -2, 2);                            /* ... thread tbl */
     lua_pop(info->L, 1);                                        /* ... thread */
     poppath(info);
     UNLOCK(thread);
@@ -1996,7 +2069,7 @@
 persist_typed(Info *info, int type) {                 /* perms reftbl ... obj */
   eris_ifassert(const int top = lua_gettop(info->L));
   if (info->level >= info->maxComplexity) {
-    eris_error(info, "object too complex");
+    eris_error(info, ERIS_ERR_COMPLEXITY);
   }
   ++info->level;
 
@@ -2033,7 +2106,7 @@
       p_upval(info);
       break;
     default:
-      eris_error(info, "trying to persist unknown type");
+      eris_error(info, ERIS_ERR_TYPEP, type);
   }                                                   /* perms reftbl ... obj */
 
   --info->level;
@@ -2130,13 +2203,14 @@
   if (lua_isnil(info->L, -1)) {                       /* perms reftbl ... nil */
     /* Since we may need permanent values to rebuild other structures, namely
      * closures and threads, we cannot allow perms to fail unpersisting. */
-    eris_error(info, "bad permanent value (no value)");
+    eris_error(info, ERIS_ERR_SPER_UPERMNIL);
   }
   else if (lua_type(info->L, -1) != type) {            /* perms reftbl ... :( */
     /* For the same reason that we cannot allow nil we must also require the
      * unpersisted value to be of the correct type. */
-    eris_error(info, "bad permanent value (%s expected, got %s)",
-      kTypenames[type], kTypenames[lua_type(info->L, -1)]);
+    const char *want = kTypenames[type];
+    const char *have = kTypenames[lua_type(info->L, -1)];
+    eris_error(info, ERIS_ERR_SPER_UPERM, want, have);
   }                                                   /* perms reftbl ... obj */
   /* Create the entry in the reftable. */
   lua_pushvalue(info->L, -1);                     /* perms reftbl ... obj obj */
@@ -2147,7 +2221,7 @@
 unpersist(Info *info) {                                   /* perms reftbl ... */
   eris_ifassert(const int top = lua_gettop(info->L));
   if (info->level >= info->maxComplexity) {
-    eris_error(info, "object too complex");
+    eris_error(info, ERIS_ERR_COMPLEXITY);
   }
   ++info->level;
 
@@ -2158,7 +2232,7 @@
       const int reference = typeOrReference - ERIS_REFERENCE_OFFSET;
       lua_rawgeti(info->L, REFTIDX, reference);   /* perms reftbl ud ... obj? */
       if (lua_isnil(info->L, -1)) {                 /* perms reftbl ud ... :( */
-        eris_error(info, "invalid reference #%d", reference);
+        eris_error(info, ERIS_ERR_REF, reference);
       }                                            /* perms reftbl ud ... obj */
     }
     else {
@@ -2201,7 +2275,7 @@
           u_permanent(info);
           break;
         default:
-          eris_error(info, "trying to unpersist unknown type %d", type);
+          eris_error(info, ERIS_ERR_TYPEU, type);
       }                                              /* perms reftbl ... obj? */
     }
   }
@@ -2621,6 +2695,8 @@
 
 LUA_API void
 eris_persist(lua_State *L, int perms, int value) {                    /* ...? */
+  perms = lua_absindex(L, perms);
+  value = lua_absindex(L, value);
   eris_checkstack(L, 3);
   lua_pushcfunction(L, l_persist);                           /* ... l_persist */
   lua_pushvalue(L, perms);                             /* ... l_persist perms */
@@ -2630,6 +2706,8 @@
 
 LUA_API void
 eris_unpersist(lua_State *L, int perms, int value) {                   /* ... */
+  perms = lua_absindex(L, perms);
+  value = lua_absindex(L, value);
   eris_checkstack(L, 3);
   lua_pushcfunction(L, l_unpersist);                       /* ... l_unpersist */
   lua_pushvalue(L, perms);                           /* ... l_unpersist perms */
@@ -2647,6 +2725,7 @@
 
 LUA_API void
 eris_set_setting(lua_State *L, const char *name, int value) {          /* ... */
+  value = lua_absindex(L, value);
   eris_checkstack(L, 3);
   lua_pushcfunction(L, l_settings);                         /* ... l_settings */
   lua_pushstring(L, name);                             /* ... l_settings name */

=== modified file 'src/third_party/eris/eris.h'
--- src/third_party/eris/eris.h	2013-10-17 07:22:46 +0000
+++ src/third_party/eris/eris.h	2016-04-07 18:16:25 +0000
@@ -1,6 +1,6 @@
 /*
-Eris - Heavy-duty persistence for Lua 5.2.2 - Based on Pluto
-Copyright (c) 2013 by Florian Nuecke.
+Eris - Heavy-duty persistence for Lua 5.2.4 - Based on Pluto
+Copyright (c) 2013-2015 by Florian Nuecke.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,11 @@
 #ifndef ERIS_H
 #define ERIS_H
 
+#define ERIS_VERSION_MAJOR  "1"
+#define ERIS_VERSION_MINOR  "1"
+#define ERIS_VERSION_NUM    101
+#define ERIS_VERSION_RELEASE  "0"
+
 /*
 ** ==================================================================
 ** API

=== modified file 'src/third_party/eris/ldblib.c'
--- src/third_party/eris/ldblib.c	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/ldblib.c	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: ldblib.c,v 1.132.1.1 2013/04/12 18:48:47 roberto Exp $
+** $Id: ldblib.c,v 1.132.1.2 2015/02/19 17:16:55 roberto Exp $
 ** Interface from Lua to its debug API
 ** See Copyright Notice in lua.h
 */
@@ -21,6 +21,11 @@
 #define HOOKKEY		"_HKEY"
 
 
+static void checkstack (lua_State *L, lua_State *L1, int n) {
+  if (L != L1 && !lua_checkstack(L1, n))
+    luaL_error(L, "stack overflow");
+}
+
 
 static int db_getregistry (lua_State *L) {
   lua_pushvalue(L, LUA_REGISTRYINDEX);
@@ -114,6 +119,7 @@
   int arg;
   lua_State *L1 = getthread(L, &arg);
   const char *options = luaL_optstring(L, arg+2, "flnStu");
+  checkstack(L, L1, 3);
   if (lua_isnumber(L, arg+1)) {
     if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
       lua_pushnil(L);  /* level out of range */
@@ -173,6 +179,7 @@
   else {  /* stack-level argument */
     if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar))  /* out of range? */
       return luaL_argerror(L, arg+1, "level out of range");
+    checkstack(L, L1, 1);
     name = lua_getlocal(L1, &ar, nvar);
     if (name) {
       lua_xmove(L1, L, 1);  /* push local value */
@@ -196,6 +203,7 @@
     return luaL_argerror(L, arg+1, "level out of range");
   luaL_checkany(L, arg+3);
   lua_settop(L, arg+3);
+  checkstack(L, L1, 1);
   lua_xmove(L, L1, 1);
   lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
   return 1;
@@ -313,6 +321,7 @@
     lua_pushvalue(L, -1);
     lua_setmetatable(L, -2);  /* setmetatable(hooktable) = hooktable */
   }
+  checkstack(L, L1, 1);
   lua_pushthread(L1); lua_xmove(L1, L, 1);
   lua_pushvalue(L, arg+1);
   lua_rawset(L, -3);  /* set new hook */
@@ -331,6 +340,7 @@
     lua_pushliteral(L, "external hook");
   else {
     gethooktable(L);
+    checkstack(L, L1, 1);
     lua_pushthread(L1); lua_xmove(L1, L, 1);
     lua_rawget(L, -2);   /* get hook */
     lua_remove(L, -2);  /* remove hook table */

=== modified file 'src/third_party/eris/ldebug.c'
--- src/third_party/eris/ldebug.c	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/ldebug.c	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: ldebug.c,v 2.90.1.3 2013/05/16 16:04:15 roberto Exp $
+** $Id: ldebug.c,v 2.90.1.4 2015/02/19 17:05:13 roberto Exp $
 ** Debug Interface
 ** See Copyright Notice in lua.h
 */
@@ -47,6 +47,16 @@
 }
 
 
+static void swapextra (lua_State *L) {
+  if (L->status == LUA_YIELD) {
+    CallInfo *ci = L->ci;  /* get function that yielded */
+    StkId temp = ci->func;  /* exchange its 'func' and 'extra' values */
+    ci->func = restorestack(L, ci->extra);
+    ci->extra = savestack(L, temp);
+  }
+}
+
+
 /*
 ** this function can be called asynchronous (e.g. during a signal)
 */
@@ -144,6 +154,7 @@
 LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
   const char *name;
   lua_lock(L);
+  swapextra(L);
   if (ar == NULL) {  /* information about non-active function? */
     if (!isLfunction(L->top - 1))  /* not a Lua function? */
       name = NULL;
@@ -158,6 +169,7 @@
       api_incr_top(L);
     }
   }
+  swapextra(L);
   lua_unlock(L);
   return name;
 }
@@ -165,11 +177,14 @@
 
 LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
   StkId pos = 0;  /* to avoid warnings */
-  const char *name = findlocal(L, ar->i_ci, n, &pos);
+  const char *name;
   lua_lock(L);
+  swapextra(L);
+  name = findlocal(L, ar->i_ci, n, &pos);
   if (name)
     setobjs2s(L, pos, L->top - 1);
   L->top--;  /* pop value */
+  swapextra(L);
   lua_unlock(L);
   return name;
 }
@@ -269,6 +284,7 @@
   CallInfo *ci;
   StkId func;
   lua_lock(L);
+  swapextra(L);
   if (*what == '>') {
     ci = NULL;
     func = L->top - 1;
@@ -287,6 +303,7 @@
     setobjs2s(L, L->top, func);
     api_incr_top(L);
   }
+  swapextra(L);
   if (strchr(what, 'L'))
     collectvalidlines(L, cl);
   lua_unlock(L);

=== modified file 'src/third_party/eris/lgc.c'
--- src/third_party/eris/lgc.c	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/lgc.c	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.140.1.2 2013/04/26 18:22:05 roberto Exp $
+** $Id: lgc.c,v 2.140.1.3 2014/09/01 16:55:08 roberto Exp $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -403,7 +403,7 @@
       reallymarkobject(g, gcvalue(gval(n)));  /* mark it now */
     }
   }
-  if (prop)
+  if (g->gcstate != GCSatomic || prop)
     linktable(h, &g->ephemeron);  /* have to propagate again */
   else if (hasclears)  /* does table have white keys? */
     linktable(h, &g->allweak);  /* may have to clean white keys */

=== modified file 'src/third_party/eris/llex.c'
--- src/third_party/eris/llex.c	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/llex.c	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: llex.c,v 2.63.1.2 2013/08/30 15:49:41 roberto Exp $
+** $Id: llex.c,v 2.63.1.3 2015/02/09 17:56:34 roberto Exp $
 ** Lexical Analyzer
 ** See Copyright Notice in lua.h
 */
@@ -152,7 +152,7 @@
   if (currIsNewline(ls) && ls->current != old)
     next(ls);  /* skip `\n\r' or `\r\n' */
   if (++ls->linenumber >= MAX_INT)
-    luaX_syntaxerror(ls, "chunk has too many lines");
+    lexerror(ls, "chunk has too many lines", 0);
 }
 
 

=== modified file 'src/third_party/eris/lobject.h'
--- src/third_party/eris/lobject.h	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/lobject.h	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: lobject.h,v 2.71.1.1 2013/04/12 18:48:47 roberto Exp $
+** $Id: lobject.h,v 2.71.1.2 2014/05/07 14:14:58 roberto Exp $
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 */
@@ -561,12 +561,12 @@
   CommonHeader;
   lu_byte flags;  /* 1<<p means tagmethod(p) is not present */
   lu_byte lsizenode;  /* log2 of size of `node' array */
-  struct Table *metatable;
+  int sizearray;  /* size of `array' array */
   TValue *array;  /* array part */
   Node *node;
   Node *lastfree;  /* any free position is before this position */
+  struct Table *metatable;
   GCObject *gclist;
-  int sizearray;  /* size of `array' array */
 } Table;
 
 

=== modified file 'src/third_party/eris/lopcodes.h'
--- src/third_party/eris/lopcodes.h	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/lopcodes.h	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: lopcodes.h,v 1.142.1.1 2013/04/12 18:48:47 roberto Exp $
+** $Id: lopcodes.h,v 1.142.1.2 2014/10/20 18:32:09 roberto Exp $
 ** Opcodes for Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -196,7 +196,7 @@
 
 OP_CONCAT,/*	A B C	R(A) := R(B).. ... ..R(C)			*/
 
-OP_JMP,/*	A sBx	pc+=sBx; if (A) close all upvalues >= R(A) + 1	*/
+OP_JMP,/*	A sBx	pc+=sBx; if (A) close all upvalues >= R(A - 1)	*/
 OP_EQ,/*	A B C	if ((RK(B) == RK(C)) ~= A) then pc++		*/
 OP_LT,/*	A B C	if ((RK(B) <  RK(C)) ~= A) then pc++		*/
 OP_LE,/*	A B C	if ((RK(B) <= RK(C)) ~= A) then pc++		*/

=== modified file 'src/third_party/eris/ltablib.c'
--- src/third_party/eris/ltablib.c	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/ltablib.c	2016-04-07 18:16:25 +0000
@@ -1,10 +1,11 @@
 /*
-** $Id: ltablib.c,v 1.65.1.1 2013/04/12 18:48:47 roberto Exp $
+** $Id: ltablib.c,v 1.65.1.2 2014/05/07 16:32:55 roberto Exp $
 ** Library for Table Manipulation
 ** See Copyright Notice in lua.h
 */
 
 
+#include <limits.h>
 #include <stddef.h>
 
 #define ltablib_c
@@ -134,13 +135,14 @@
 
 
 static int unpack (lua_State *L) {
-  int i, e, n;
+  int i, e;
+  unsigned int n;
   luaL_checktype(L, 1, LUA_TTABLE);
   i = luaL_optint(L, 2, 1);
   e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1));
   if (i > e) return 0;  /* empty range */
-  n = e - i + 1;  /* number of elements */
-  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */
+  n = (unsigned int)e - (unsigned int)i;  /* number of elements minus 1 */
+  if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n))
     return luaL_error(L, "too many results to unpack");
   lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */
   while (i++ < e)  /* push arg[i + 1...e] */

=== added file 'src/third_party/eris/lua.c'
--- src/third_party/eris/lua.c	1970-01-01 00:00:00 +0000
+++ src/third_party/eris/lua.c	2016-04-07 18:16:25 +0000
@@ -0,0 +1,497 @@
+/*
+** $Id: lua.c,v 1.206.1.1 2013/04/12 18:48:47 roberto Exp $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lua_c
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#if !defined(LUA_PROMPT)
+#define LUA_PROMPT		"> "
+#define LUA_PROMPT2		">> "
+#endif
+
+#if !defined(LUA_PROGNAME)
+#define LUA_PROGNAME		"lua"
+#endif
+
+#if !defined(LUA_MAXINPUT)
+#define LUA_MAXINPUT		512
+#endif
+
+#if !defined(LUA_INIT)
+#define LUA_INIT		"LUA_INIT"
+#endif
+
+#define LUA_INITVERSION  \
+	LUA_INIT "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
+
+
+/*
+** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+** is, whether we're running lua interactively).
+*/
+#if defined(LUA_USE_ISATTY)
+#include <unistd.h>
+#define lua_stdin_is_tty()	isatty(0)
+#elif defined(LUA_WIN)
+#include <io.h>
+#include <stdio.h>
+#define lua_stdin_is_tty()	_isatty(_fileno(stdin))
+#else
+#define lua_stdin_is_tty()	1  /* assume stdin is a tty */
+#endif
+
+
+/*
+** lua_readline defines how to show a prompt and then read a line from
+** the standard input.
+** lua_saveline defines how to "save" a read line in a "history".
+** lua_freeline defines how to free a line read by lua_readline.
+*/
+#if defined(LUA_USE_READLINE)
+
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#define lua_readline(L,b,p)	((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,idx) \
+        if (lua_rawlen(L,idx) > 0)  /* non-empty line? */ \
+          add_history(lua_tostring(L, idx));  /* add it to history */
+#define lua_freeline(L,b)	((void)L, free(b))
+
+#elif !defined(lua_readline)
+
+#define lua_readline(L,b,p) \
+        ((void)L, fputs(p, stdout), fflush(stdout),  /* show prompt */ \
+        fgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */
+#define lua_saveline(L,idx)	{ (void)L; (void)idx; }
+#define lua_freeline(L,b)	{ (void)L; (void)b; }
+
+#endif
+
+
+
+
+static lua_State *globalL = NULL;
+
+static const char *progname = LUA_PROGNAME;
+
+
+
+static void lstop (lua_State *L, lua_Debug *ar) {
+  (void)ar;  /* unused arg. */
+  lua_sethook(L, NULL, 0, 0);
+  luaL_error(L, "interrupted!");
+}
+
+
+static void laction (int i) {
+  signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
+                              terminate process (default action) */
+  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+
+
+static void print_usage (const char *badoption) {
+  luai_writestringerror("%s: ", progname);
+  if (badoption[1] == 'e' || badoption[1] == 'l')
+    luai_writestringerror("'%s' needs argument\n", badoption);
+  else
+    luai_writestringerror("unrecognized option '%s'\n", badoption);
+  luai_writestringerror(
+  "usage: %s [options] [script [args]]\n"
+  "Available options are:\n"
+  "  -e stat  execute string " LUA_QL("stat") "\n"
+  "  -i       enter interactive mode after executing " LUA_QL("script") "\n"
+  "  -l name  require library " LUA_QL("name") "\n"
+  "  -v       show version information\n"
+  "  -E       ignore environment variables\n"
+  "  --       stop handling options\n"
+  "  -        stop handling options and execute stdin\n"
+  ,
+  progname);
+}
+
+
+static void l_message (const char *pname, const char *msg) {
+  if (pname) luai_writestringerror("%s: ", pname);
+  luai_writestringerror("%s\n", msg);
+}
+
+
+static int report (lua_State *L, int status) {
+  if (status != LUA_OK && !lua_isnil(L, -1)) {
+    const char *msg = lua_tostring(L, -1);
+    if (msg == NULL) msg = "(error object is not a string)";
+    l_message(progname, msg);
+    lua_pop(L, 1);
+    /* force a complete garbage collection in case of errors */
+    lua_gc(L, LUA_GCCOLLECT, 0);
+  }
+  return status;
+}
+
+
+/* the next function is called unprotected, so it must avoid errors */
+static void finalreport (lua_State *L, int status) {
+  if (status != LUA_OK) {
+    const char *msg = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1)
+                                                       : NULL;
+    if (msg == NULL) msg = "(error object is not a string)";
+    l_message(progname, msg);
+    lua_pop(L, 1);
+  }
+}
+
+
+static int traceback (lua_State *L) {
+  const char *msg = lua_tostring(L, 1);
+  if (msg)
+    luaL_traceback(L, L, msg, 1);
+  else if (!lua_isnoneornil(L, 1)) {  /* is there an error object? */
+    if (!luaL_callmeta(L, 1, "__tostring"))  /* try its 'tostring' metamethod */
+      lua_pushliteral(L, "(no error message)");
+  }
+  return 1;
+}
+
+
+static int docall (lua_State *L, int narg, int nres) {
+  int status;
+  int base = lua_gettop(L) - narg;  /* function index */
+  lua_pushcfunction(L, traceback);  /* push traceback function */
+  lua_insert(L, base);  /* put it under chunk and args */
+  globalL = L;  /* to be available to 'laction' */
+  signal(SIGINT, laction);
+  status = lua_pcall(L, narg, nres, base);
+  signal(SIGINT, SIG_DFL);
+  lua_remove(L, base);  /* remove traceback function */
+  return status;
+}
+
+
+static void print_version (void) {
+  luai_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
+  luai_writeline();
+}
+
+
+static int getargs (lua_State *L, char **argv, int n) {
+  int narg;
+  int i;
+  int argc = 0;
+  while (argv[argc]) argc++;  /* count total number of arguments */
+  narg = argc - (n + 1);  /* number of arguments to the script */
+  luaL_checkstack(L, narg + 3, "too many arguments to script");
+  for (i=n+1; i < argc; i++)
+    lua_pushstring(L, argv[i]);
+  lua_createtable(L, narg, n + 1);
+  for (i=0; i < argc; i++) {
+    lua_pushstring(L, argv[i]);
+    lua_rawseti(L, -2, i - n);
+  }
+  return narg;
+}
+
+
+static int dofile (lua_State *L, const char *name) {
+  int status = luaL_loadfile(L, name);
+  if (status == LUA_OK) status = docall(L, 0, 0);
+  return report(L, status);
+}
+
+
+static int dostring (lua_State *L, const char *s, const char *name) {
+  int status = luaL_loadbuffer(L, s, strlen(s), name);
+  if (status == LUA_OK) status = docall(L, 0, 0);
+  return report(L, status);
+}
+
+
+static int dolibrary (lua_State *L, const char *name) {
+  int status;
+  lua_getglobal(L, "require");
+  lua_pushstring(L, name);
+  status = docall(L, 1, 1);  /* call 'require(name)' */
+  if (status == LUA_OK)
+    lua_setglobal(L, name);  /* global[name] = require return */
+  return report(L, status);
+}
+
+
+static const char *get_prompt (lua_State *L, int firstline) {
+  const char *p;
+  lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
+  p = lua_tostring(L, -1);
+  if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
+  return p;
+}
+
+/* mark in error messages for incomplete statements */
+#define EOFMARK		"<eof>"
+#define marklen		(sizeof(EOFMARK)/sizeof(char) - 1)
+
+static int incomplete (lua_State *L, int status) {
+  if (status == LUA_ERRSYNTAX) {
+    size_t lmsg;
+    const char *msg = lua_tolstring(L, -1, &lmsg);
+    if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
+      lua_pop(L, 1);
+      return 1;
+    }
+  }
+  return 0;  /* else... */
+}
+
+
+static int pushline (lua_State *L, int firstline) {
+  char buffer[LUA_MAXINPUT];
+  char *b = buffer;
+  size_t l;
+  const char *prmt = get_prompt(L, firstline);
+  int readstatus = lua_readline(L, b, prmt);
+  lua_pop(L, 1);  /* remove result from 'get_prompt' */
+  if (readstatus == 0)
+    return 0;  /* no input */
+  l = strlen(b);
+  if (l > 0 && b[l-1] == '\n')  /* line ends with newline? */
+    b[l-1] = '\0';  /* remove it */
+  if (firstline && b[0] == '=')  /* first line starts with `=' ? */
+    lua_pushfstring(L, "return %s", b+1);  /* change it to `return' */
+  else
+    lua_pushstring(L, b);
+  lua_freeline(L, b);
+  return 1;
+}
+
+
+static int loadline (lua_State *L) {
+  int status;
+  lua_settop(L, 0);
+  if (!pushline(L, 1))
+    return -1;  /* no input */
+  for (;;) {  /* repeat until gets a complete line */
+    size_t l;
+    const char *line = lua_tolstring(L, 1, &l);
+    status = luaL_loadbuffer(L, line, l, "=stdin");
+    if (!incomplete(L, status)) break;  /* cannot try to add lines? */
+    if (!pushline(L, 0))  /* no more input? */
+      return -1;
+    lua_pushliteral(L, "\n");  /* add a new line... */
+    lua_insert(L, -2);  /* ...between the two lines */
+    lua_concat(L, 3);  /* join them */
+  }
+  lua_saveline(L, 1);
+  lua_remove(L, 1);  /* remove line */
+  return status;
+}
+
+
+static void dotty (lua_State *L) {
+  int status;
+  const char *oldprogname = progname;
+  progname = NULL;
+  while ((status = loadline(L)) != -1) {
+    if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET);
+    report(L, status);
+    if (status == LUA_OK && lua_gettop(L) > 0) {  /* any result to print? */
+      luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
+      lua_getglobal(L, "print");
+      lua_insert(L, 1);
+      if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != LUA_OK)
+        l_message(progname, lua_pushfstring(L,
+                               "error calling " LUA_QL("print") " (%s)",
+                               lua_tostring(L, -1)));
+    }
+  }
+  lua_settop(L, 0);  /* clear stack */
+  luai_writeline();
+  progname = oldprogname;
+}
+
+
+static int handle_script (lua_State *L, char **argv, int n) {
+  int status;
+  const char *fname;
+  int narg = getargs(L, argv, n);  /* collect arguments */
+  lua_setglobal(L, "arg");
+  fname = argv[n];
+  if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
+    fname = NULL;  /* stdin */
+  status = luaL_loadfile(L, fname);
+  lua_insert(L, -(narg+1));
+  if (status == LUA_OK)
+    status = docall(L, narg, LUA_MULTRET);
+  else
+    lua_pop(L, narg);
+  return report(L, status);
+}
+
+
+/* check that argument has no extra characters at the end */
+#define noextrachars(x)		{if ((x)[2] != '\0') return -1;}
+
+
+/* indices of various argument indicators in array args */
+#define has_i		0	/* -i */
+#define has_v		1	/* -v */
+#define has_e		2	/* -e */
+#define has_E		3	/* -E */
+
+#define num_has		4	/* number of 'has_*' */
+
+
+static int collectargs (char **argv, int *args) {
+  int i;
+  for (i = 1; argv[i] != NULL; i++) {
+    if (argv[i][0] != '-')  /* not an option? */
+        return i;
+    switch (argv[i][1]) {  /* option */
+      case '-':
+        noextrachars(argv[i]);
+        return (argv[i+1] != NULL ? i+1 : 0);
+      case '\0':
+        return i;
+      case 'E':
+        args[has_E] = 1;
+        break;
+      case 'i':
+        noextrachars(argv[i]);
+        args[has_i] = 1;  /* go through */
+      case 'v':
+        noextrachars(argv[i]);
+        args[has_v] = 1;
+        break;
+      case 'e':
+        args[has_e] = 1;  /* go through */
+      case 'l':  /* both options need an argument */
+        if (argv[i][2] == '\0') {  /* no concatenated argument? */
+          i++;  /* try next 'argv' */
+          if (argv[i] == NULL || argv[i][0] == '-')
+            return -(i - 1);  /* no next argument or it is another option */
+        }
+        break;
+      default:  /* invalid option; return its index... */
+        return -i;  /* ...as a negative value */
+    }
+  }
+  return 0;
+}
+
+
+static int runargs (lua_State *L, char **argv, int n) {
+  int i;
+  for (i = 1; i < n; i++) {
+    lua_assert(argv[i][0] == '-');
+    switch (argv[i][1]) {  /* option */
+      case 'e': {
+        const char *chunk = argv[i] + 2;
+        if (*chunk == '\0') chunk = argv[++i];
+        lua_assert(chunk != NULL);
+        if (dostring(L, chunk, "=(command line)") != LUA_OK)
+          return 0;
+        break;
+      }
+      case 'l': {
+        const char *filename = argv[i] + 2;
+        if (*filename == '\0') filename = argv[++i];
+        lua_assert(filename != NULL);
+        if (dolibrary(L, filename) != LUA_OK)
+          return 0;  /* stop if file fails */
+        break;
+      }
+      default: break;
+    }
+  }
+  return 1;
+}
+
+
+static int handle_luainit (lua_State *L) {
+  const char *name = "=" LUA_INITVERSION;
+  const char *init = getenv(name + 1);
+  if (init == NULL) {
+    name = "=" LUA_INIT;
+    init = getenv(name + 1);  /* try alternative name */
+  }
+  if (init == NULL) return LUA_OK;
+  else if (init[0] == '@')
+    return dofile(L, init+1);
+  else
+    return dostring(L, init, name);
+}
+
+
+static int pmain (lua_State *L) {
+  int argc = (int)lua_tointeger(L, 1);
+  char **argv = (char **)lua_touserdata(L, 2);
+  int script;
+  int args[num_has];
+  args[has_i] = args[has_v] = args[has_e] = args[has_E] = 0;
+  if (argv[0] && argv[0][0]) progname = argv[0];
+  script = collectargs(argv, args);
+  if (script < 0) {  /* invalid arg? */
+    print_usage(argv[-script]);
+    return 0;
+  }
+  if (args[has_v]) print_version();
+  if (args[has_E]) {  /* option '-E'? */
+    lua_pushboolean(L, 1);  /* signal for libraries to ignore env. vars. */
+    lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+  }
+  /* open standard libraries */
+  luaL_checkversion(L);
+  lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */
+  luaL_openlibs(L);  /* open libraries */
+  lua_gc(L, LUA_GCRESTART, 0);
+  if (!args[has_E] && handle_luainit(L) != LUA_OK)
+    return 0;  /* error running LUA_INIT */
+  /* execute arguments -e and -l */
+  if (!runargs(L, argv, (script > 0) ? script : argc)) return 0;
+  /* execute main script (if there is one) */
+  if (script && handle_script(L, argv, script) != LUA_OK) return 0;
+  if (args[has_i])  /* -i option? */
+    dotty(L);
+  else if (script == 0 && !args[has_e] && !args[has_v]) {  /* no arguments? */
+    if (lua_stdin_is_tty()) {
+      print_version();
+      dotty(L);
+    }
+    else dofile(L, NULL);  /* executes stdin as a file */
+  }
+  lua_pushboolean(L, 1);  /* signal no errors */
+  return 1;
+}
+
+
+int main (int argc, char **argv) {
+  int status, result;
+  lua_State *L = luaL_newstate();  /* create state */
+  if (L == NULL) {
+    l_message(argv[0], "cannot create state: not enough memory");
+    return EXIT_FAILURE;
+  }
+  /* call 'pmain' in protected mode */
+  lua_pushcfunction(L, &pmain);
+  lua_pushinteger(L, argc);  /* 1st argument */
+  lua_pushlightuserdata(L, argv); /* 2nd argument */
+  status = lua_pcall(L, 2, 1, 0);
+  result = lua_toboolean(L, -1);  /* get result */
+  finalreport(L, status);
+  lua_close(L);
+  return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+

=== modified file 'src/third_party/eris/lua.h'
--- src/third_party/eris/lua.h	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/lua.h	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: lua.h,v 1.285.1.2 2013/11/11 12:09:16 roberto Exp $
+** $Id: lua.h,v 1.285.1.4 2015/02/21 14:04:50 roberto Exp $
 ** Lua - A Scripting Language
 ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
 ** See Copyright Notice at the end of this file
@@ -19,11 +19,11 @@
 #define LUA_VERSION_MAJOR	"5"
 #define LUA_VERSION_MINOR	"2"
 #define LUA_VERSION_NUM		502
-#define LUA_VERSION_RELEASE	"3"
+#define LUA_VERSION_RELEASE	"4"
 
 #define LUA_VERSION	"Lua+Eris " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
 #define LUA_RELEASE	LUA_VERSION "." LUA_VERSION_RELEASE
-#define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2013 Lua.org, PUC-Rio"
+#define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2015 Lua.org, PUC-Rio"
 #define LUA_AUTHORS	"R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
 
 
@@ -418,7 +418,7 @@
 
 
 /******************************************************************************
-* Copyright (C) 1994-2013 Lua.org, PUC-Rio.
+* Copyright (C) 1994-2015 Lua.org, PUC-Rio.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the

=== added file 'src/third_party/eris/luac.c'
--- src/third_party/eris/luac.c	1970-01-01 00:00:00 +0000
+++ src/third_party/eris/luac.c	2016-04-07 18:16:25 +0000
@@ -0,0 +1,432 @@
+/*
+** $Id: luac.c,v 1.69 2011/11/29 17:46:33 lhf Exp $
+** Lua compiler (saves bytecodes to files; also list bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lundump.h"
+
+static void PrintFunction(const Proto* f, int full);
+#define luaU_print	PrintFunction
+
+#define PROGNAME	"luac"		/* default program name */
+#define OUTPUT		PROGNAME ".out"	/* default output file */
+
+static int listing=0;			/* list bytecodes? */
+static int dumping=1;			/* dump bytecodes? */
+static int stripping=0;			/* strip debug information? */
+static char Output[]={ OUTPUT };	/* default output file name */
+static const char* output=Output;	/* actual output file name */
+static const char* progname=PROGNAME;	/* actual program name */
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+  fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
+ else
+  fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+  "usage: %s [options] [filenames]\n"
+  "Available options are:\n"
+  "  -l       list (use -l -l for full listing)\n"
+  "  -o name  output to file " LUA_QL("name") " (default is \"%s\")\n"
+  "  -p       parse only\n"
+  "  -s       strip debug information\n"
+  "  -v       show version information\n"
+  "  --       stop handling options\n"
+  "  -        stop handling options and process stdin\n"
+  ,progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s)	(strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+  if (*argv[i]!='-')			/* end of options; keep it */
+   break;
+  else if (IS("--"))			/* end of options; skip it */
+  {
+   ++i;
+   if (version) ++version;
+   break;
+  }
+  else if (IS("-"))			/* end of options; use stdin */
+   break;
+  else if (IS("-l"))			/* list */
+   ++listing;
+  else if (IS("-o"))			/* output file */
+  {
+   output=argv[++i];
+   if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
+    usage(LUA_QL("-o") " needs argument");
+   if (IS("-")) output=NULL;
+  }
+  else if (IS("-p"))			/* parse only */
+   dumping=0;
+  else if (IS("-s"))			/* strip debug information */
+   stripping=1;
+  else if (IS("-v"))			/* show version */
+   ++version;
+  else					/* unknown option */
+   usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+  dumping=0;
+  argv[--i]=Output;
+ }
+ if (version)
+ {
+  printf("%s\n",LUA_COPYRIGHT);
+  if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+#define FUNCTION "(function()end)();"
+
+static const char* reader(lua_State *L, void *ud, size_t *size)
+{
+ UNUSED(L);
+ if ((*(int*)ud)--)
+ {
+  *size=sizeof(FUNCTION)-1;
+  return FUNCTION;
+ }
+ else
+ {
+  *size=0;
+  return NULL;
+ }
+}
+
+#define toproto(L,i) getproto(L->top+(i))
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+  return toproto(L,-1);
+ else
+ {
+  Proto* f;
+  int i=n;
+  if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
+  f=toproto(L,-1);
+  for (i=0; i<n; i++)
+  {
+   f->p[i]=toproto(L,i-n-1);
+   if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
+  }
+  f->sizelineinfo=0;
+  return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+static int pmain(lua_State* L)
+{
+ int argc=(int)lua_tointeger(L,1);
+ char** argv=(char**)lua_touserdata(L,2);
+ const Proto* f;
+ int i;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i<argc; i++)
+ {
+  const char* filename=IS("-") ? NULL : argv[i];
+  if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+  FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+  if (D==NULL) cannot("open");
+  lua_lock(L);
+  luaU_dump(L,f,writer,D,stripping);
+  lua_unlock(L);
+  if (ferror(D)) cannot("write");
+  if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=luaL_newstate();
+ if (L==NULL) fatal("cannot create state: not enough memory");
+ lua_pushcfunction(L,&pmain);
+ lua_pushinteger(L,argc);
+ lua_pushlightuserdata(L,argv);
+ if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
+
+/*
+** $Id: print.c,v 1.69 2013/07/04 01:03:46 lhf Exp $
+** print bytecodes
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+
+#define VOID(p)		((const void*)(p))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=ts->tsv.len;
+ printf("%c",'"');
+ for (i=0; i<n; i++)
+ {
+  int c=(int)(unsigned char)s[i];
+  switch (c)
+  {
+   case '"':  printf("\\\""); break;
+   case '\\': printf("\\\\"); break;
+   case '\a': printf("\\a"); break;
+   case '\b': printf("\\b"); break;
+   case '\f': printf("\\f"); break;
+   case '\n': printf("\\n"); break;
+   case '\r': printf("\\r"); break;
+   case '\t': printf("\\t"); break;
+   case '\v': printf("\\v"); break;
+   default:	if (isprint(c))
+   			printf("%c",c);
+		else
+			printf("\\%03d",c);
+  }
+ }
+ printf("%c",'"');
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttypenv(o))
+ {
+  case LUA_TNIL:
+	printf("nil");
+	break;
+  case LUA_TBOOLEAN:
+	printf(bvalue(o) ? "true" : "false");
+	break;
+  case LUA_TNUMBER:
+	printf(LUA_NUMBER_FMT,nvalue(o));
+	break;
+  case LUA_TSTRING:
+	PrintString(rawtsvalue(o));
+	break;
+  default:				/* cannot happen */
+	printf("? type=%d",ttype(o));
+	break;
+ }
+}
+
+#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
+#define MYK(x)		(-1-(x))
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+  Instruction i=code[pc];
+  OpCode o=GET_OPCODE(i);
+  int a=GETARG_A(i);
+  int b=GETARG_B(i);
+  int c=GETARG_C(i);
+  int ax=GETARG_Ax(i);
+  int bx=GETARG_Bx(i);
+  int sbx=GETARG_sBx(i);
+  int line=getfuncline(f,pc);
+  printf("\t%d\t",pc+1);
+  if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+  printf("%-9s\t",luaP_opnames[o]);
+  switch (getOpMode(o))
+  {
+   case iABC:
+    printf("%d",a);
+    if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b);
+    if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c);
+    break;
+   case iABx:
+    printf("%d",a);
+    if (getBMode(o)==OpArgK) printf(" %d",MYK(bx));
+    if (getBMode(o)==OpArgU) printf(" %d",bx);
+    break;
+   case iAsBx:
+    printf("%d %d",a,sbx);
+    break;
+   case iAx:
+    printf("%d",MYK(ax));
+    break;
+  }
+  switch (o)
+  {
+   case OP_LOADK:
+    printf("\t; "); PrintConstant(f,bx);
+    break;
+   case OP_GETUPVAL:
+   case OP_SETUPVAL:
+    printf("\t; %s",UPVALNAME(b));
+    break;
+   case OP_GETTABUP:
+    printf("\t; %s",UPVALNAME(b));
+    if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_SETTABUP:
+    printf("\t; %s",UPVALNAME(a));
+    if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); }
+    if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_GETTABLE:
+   case OP_SELF:
+    if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
+    break;
+   case OP_SETTABLE:
+   case OP_ADD:
+   case OP_SUB:
+   case OP_MUL:
+   case OP_DIV:
+   case OP_POW:
+   case OP_EQ:
+   case OP_LT:
+   case OP_LE:
+    if (ISK(b) || ISK(c))
+    {
+     printf("\t; ");
+     if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
+     printf(" ");
+     if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
+    }
+    break;
+   case OP_JMP:
+   case OP_FORLOOP:
+   case OP_FORPREP:
+   case OP_TFORLOOP:
+    printf("\t; to %d",sbx+pc+2);
+    break;
+   case OP_CLOSURE:
+    printf("\t; %p",VOID(f->p[bx]));
+    break;
+   case OP_SETLIST:
+    if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c);
+    break;
+   case OP_EXTRAARG:
+    printf("\t; "); PrintConstant(f,ax);
+    break;
+   default:
+    break;
+  }
+  printf("\n");
+ }
+}
+
+#define SS(x)	((x==1)?"":"s")
+#define S(x)	(int)(x),SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=f->source ? getstr(f->source) : "=?";
+ if (*s=='@' || *s=='=')
+  s++;
+ else if (*s==LUA_SIGNATURE[0])
+  s="(bstring)";
+ else
+  s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
+ 	(f->linedefined==0)?"main":"function",s,
+	f->linedefined,f->lastlinedefined,
+	S(f->sizecode),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+	(int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
+	S(f->maxstacksize),S(f->sizeupvalues));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+	S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintDebug(const Proto* f)
+{
+ int i,n;
+ n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t",i+1);
+  PrintConstant(f,i);
+  printf("\n");
+ }
+ n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t%s\t%d\t%d\n",
+  i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+ n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+  printf("\t%d\t%s\t%d\t%d\n",
+  i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
+ }
+}
+
+static void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full) PrintDebug(f);
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}

=== modified file 'src/third_party/eris/luaconf.h'
--- src/third_party/eris/luaconf.h	2014-02-22 14:47:28 +0000
+++ src/third_party/eris/luaconf.h	2016-04-07 18:16:25 +0000
@@ -1,5 +1,5 @@
 /*
-** $Id: luaconf.h,v 1.176.1.1 2013/04/12 18:48:47 roberto Exp $
+** $Id: luaconf.h,v 1.176.1.2 2013/11/21 17:26:16 roberto Exp $
 ** Configuration file for Lua
 ** See Copyright Notice in lua.h
 */
@@ -326,7 +326,7 @@
 
 
 /*
-@@ LUA_INT32 is an signed integer with exactly 32 bits.
+@@ LUA_INT32 is a signed integer with exactly 32 bits.
 @@ LUAI_UMEM is an unsigned integer big enough to count the total
 @* memory used by Lua.
 @@ LUAI_MEM is a signed integer big enough to count the total memory
@@ -350,7 +350,7 @@
 /*
 @@ LUAI_MAXSTACK limits the size of the Lua stack.
 ** CHANGE it if you need a different limit. This limit is arbitrary;
-** its only purpose is to stop Lua to consume unlimited stack
+** its only purpose is to stop Lua from consuming unlimited stack
 ** space (and to reserve some numbers for pseudo-indices).
 */
 #if LUAI_BITSINT >= 32


Follow ups