1 /*
2 Eris - Heavy-duty persistence for Lua 5.3.4 - Based on Pluto
3 Copyright (c) 2013-2015 by Florian Nuecke.
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE.
22 */
23 
24 /* Standard library headers. */
25 #include <assert.h>
26 #include <math.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 /* Not using stdbool because Visual Studio lives in the past... */
32 #ifndef __cplusplus
33 typedef int bool;
34 #define false 0
35 #define true 1
36 #endif
37 
38 /* Mark us as part of the Lua core to get access to what we need. */
39 #define LUA_CORE
40 
41 /* Public Lua headers. */
42 #include "lapi.h"
43 #include "lua.h"
44 #include "lauxlib.h"
45 #include "lualib.h"
46 
47 /* Internal Lua headers. */
48 #include "ldebug.h"
49 #include "ldo.h"
50 #include "lfunc.h"
51 #include "lobject.h"
52 #include "lstate.h"
53 #include "lstring.h"
54 #include "lzio.h"
55 
56 /* Eris header. */
57 #include "eris.h"
58 
59 /*
60 ** {===========================================================================
61 ** Default settings.
62 ** ============================================================================
63 */
64 
65 /* Note that these are the default settings. They can be changed either from C
66  * by calling eris_g|set_setting() or from Lua using eris.settings(). */
67 
68 /* The metatable key we use to allow customized persistence for tables and
69  * userdata. */
70 static const char *const kPersistKey = "__persist";
71 
72 /* Whether to pass the IO object (reader/writer) to the special function
73  * defined in the metafield or not. This is disabled per default because it
74  * mey allow Lua scripts to do more than they should. Enable this as needed. */
75 static const bool kPassIOToPersist = false;
76 
77 /* Whether to persist debug information such as line numbers and upvalue and
78  * local variable names. */
79 static const bool kWriteDebugInformation = true;
80 
81 /* Generate a human readable "path" that is shown together with error messages
82  * to indicate where in the object the error occurred. For example:
83  * eris.persist({false, bad = setmetatable({}, {__persist = false})})
84  * Will produce: main:1: attempt to persist forbidden table (root.bad)
85  * This can be used for debugging, but is disabled per default due to the
86  * processing and memory overhead this introduces. */
87 static const bool kGeneratePath = false;
88 
89 /* The maximum object complexity. This is the number of allowed recursions when
90  * persisting or unpersisting an object, for example for nested tables. This is
91  * used to avoid segfaults when writing or reading user data. */
92 static const lua_Unsigned kMaxComplexity = 10000;
93 
94 /*
95 ** ============================================================================
96 ** Lua internals interfacing.
97 ** ============================================================================
98 */
99 
100 /* Lua internals we use. We define these as macros to make it easier to swap
101  * them out, should the need ever arise. For example, the later Pluto versions
102  * copied these function to own files (presumably to allow building it as an
103  * extra shared library). These should be all functions we use that are not
104  * declared in lua.h or lauxlib.h. If there are some still directly in the code
105  * they were missed and should be replaced with a macro added here instead. */
106 /* I'm quite sure we won't ever want to do this, because Eris needs a slightly
107  * patched Lua version to be able to persist some of the library functions,
108  * anyway: it needs to put the continuation C functions in the perms table. */
109 /* ldebug.h */
110 #define eris_ci_func(ci)		(clLvalue((ci)->func))
111 /* ldo.h */
112 #define eris_savestack savestack
113 #define eris_restorestack restorestack
114 #define eris_reallocstack luaD_reallocstack
115 /* lfunc.h */
116 #define eris_newproto luaF_newproto
117 #define eris_newLclosure luaF_newLclosure
118 #define eris_initupvals luaF_initupvals
119 #define eris_findupval luaF_findupval
120 /* lmem.h */
121 #define eris_reallocvector luaM_reallocvector
122 /* lobject.h */
123 #define eris_ttnov ttnov
124 #define eris_clLvalue clLvalue
125 #define eris_setnilvalue setnilvalue
126 #define eris_setclLvalue setclLvalue
127 #define eris_setobj setobj
128 #define eris_setsvalue2n setsvalue2n
129 /* lstate.h */
130 #define eris_isLua isLua
131 #define eris_gch gch
132 #define eris_gco2uv gco2uv
133 #define eris_obj2gco obj2gco
134 #define eris_extendCI luaE_extendCI
135 /* lstring. h */
136 #define eris_newlstr luaS_newlstr
137 /* lzio.h */
138 #define eris_initbuffer luaZ_initbuffer
139 #define eris_buffer luaZ_buffer
140 #define eris_sizebuffer luaZ_sizebuffer
141 #define eris_bufflen luaZ_bufflen
142 #define eris_init luaZ_init
143 #define eris_read luaZ_read
144 
145 /* These are required for cross-platform support, since the size of TValue may
146  * differ, so the byte offset used by savestack/restorestack in Lua it is not a
147  * valid measure. */
148 #define eris_savestackidx(L, p) ((p) - (L)->stack)
149 #define eris_restorestackidx(L, n) ((L)->stack + (n))
150 
151 /* Enabled if we have a patched version of Lua (for accessing internals). */
152 #if 1
153 
154 /* Functions in Lua libraries used to access C functions we need to add to the
155  * permanents table to fully support yielded coroutines. */
156 extern void eris_permbaselib(lua_State *L, int forUnpersist);
157 extern void eris_permcorolib(lua_State *L, int forUnpersist);
158 extern void eris_permloadlib(lua_State *L, int forUnpersist);
159 extern void eris_permiolib(lua_State *L, int forUnpersist);
160 extern void eris_permstrlib(lua_State *L, int forUnpersist);
161 
162 /* Utility macro for populating the perms table with internal C functions. */
163 #define populateperms(L, forUnpersist) {\
164   eris_permbaselib(L, forUnpersist);\
165   eris_permcorolib(L, forUnpersist);\
166   eris_permloadlib(L, forUnpersist);\
167   eris_permiolib(L, forUnpersist);\
168   eris_permstrlib(L, forUnpersist);\
169 }
170 
171 #else
172 
173 /* Does nothing if we don't have a patched version of Lua. */
174 #define populateperms(L, forUnpersist) ((void)0)
175 
176 #endif
177 
178 /*
179 ** ============================================================================
180 ** Language strings for errors.
181 ** ============================================================================
182 */
183 
184 #define ERIS_ERR_CFUNC "attempt to persist a light C function (%p)"
185 #define ERIS_ERR_COMPLEXITY "object too complex"
186 #define ERIS_ERR_HOOK "cannot persist yielded hooks"
187 #define ERIS_ERR_METATABLE "bad metatable, not nil or table"
188 #define ERIS_ERR_NOFUNC "attempt to persist unknown function type"
189 #define ERIS_ERR_READ "could not read data"
190 #define ERIS_ERR_SPER_FUNC "%s did not return a function"
191 #define ERIS_ERR_SPER_LOAD "bad unpersist function (%s expected, returned %s)"
192 #define ERIS_ERR_SPER_PROT "attempt to persist forbidden table"
193 #define ERIS_ERR_SPER_TYPE "%d not nil, boolean, or function"
194 #define ERIS_ERR_SPER_UFUNC "invalid restore function"
195 #define ERIS_ERR_SPER_UPERM "bad permanent value (%s expected, got %s)"
196 #define ERIS_ERR_SPER_UPERMNIL "bad permanent value (no value)"
197 #define ERIS_ERR_STACKBOUNDS "stack index out of bounds"
198 #define ERIS_ERR_TABLE "bad table value, got a nil value"
199 #define ERIS_ERR_THREAD "cannot persist currently running thread"
200 #define ERIS_ERR_THREADCI "invalid callinfo"
201 #define ERIS_ERR_THREADCTX "bad C continuation function"
202 #define ERIS_ERR_THREADERRF "invalid errfunc"
203 #define ERIS_ERR_THREADPC "saved program counter out of bounds"
204 #define ERIS_ERR_TRUNC_INT "int value would get truncated"
205 #define ERIS_ERR_TRUNC_SIZE "size_t value would get truncated"
206 #define ERIS_ERR_TYPE_FLOAT "unsupported lua_Number type"
207 #define ERIS_ERR_TYPE_INT "unsupported int type"
208 #define ERIS_ERR_TYPE_SIZE "unsupported size_t type"
209 #define ERIS_ERR_TYPEP "trying to persist unknown type %d"
210 #define ERIS_ERR_TYPEU "trying to unpersist unknown type %d"
211 #define ERIS_ERR_UCFUNC "bad C closure (C function expected, got %s)"
212 #define ERIS_ERR_UCFUNCNULL "bad C closure (C function expected, got null)"
213 #define ERIS_ERR_USERDATA "attempt to literally persist userdata"
214 #define ERIS_ERR_WRITE "could not write data"
215 #define ERIS_ERR_REF "invalid reference #%d. this usually means a special "\
216                       "persistence callback of a table referenced said table "\
217                       "(directly or indirectly via an upvalue)."
218 
219 /*
220 ** ============================================================================
221 ** Constants, settings, types and forward declarations.
222 ** ============================================================================
223 */
224 
225 /* The upvalue tag type was removed in Lua 5.3, but we still need it for
226  * special handling of upvalues, so we redeclare it for internal use. */
227 #define LUA_TUPVAL (LUA_TOTALTAGS + 1)
228 
229 /* The "type" we write when we persist a value via a replacement from the
230  * permanents table. This is just an arbitrary number, but it must we lower
231  * than the reference offset (below) and outside the range Lua uses for its
232  * types (> LUA_TOTALTAGS). */
233 #define ERIS_PERMANENT (LUA_TOTALTAGS + 2)
234 
235 /* This is essentially the first reference we'll use. We do this to save one
236  * field in our persisted data: if the value is smaller than this, the object
237  * itself follows, otherwise we have a reference to an already unpersisted
238  * object. Note that in the reftable the actual entries are still stored
239  * starting at the first array index to have a sequence (when unpersisting). */
240 #define ERIS_REFERENCE_OFFSET (ERIS_PERMANENT + 1)
241 
242 /* Avoids having to write the NULL all the time, plus makes it easier adding
243  * a custom error message should you ever decide you want one. */
244 #define eris_checkstack(L, n) luaL_checkstack(L, n, NULL)
245 
246 /* Used for internal consistency checks, for debugging. These are true asserts
247  * in the sense that they should never fire, even for bad inputs. */
248 #if 0
249 #define eris_assert(c) assert(c)
250 #define eris_ifassert(e) e
251 #else
252 #define eris_assert(c) ((void)0)
253 #define eris_ifassert(e) ((void)0)
254 #endif
255 
256 /* State information when persisting an object. */
257 typedef struct PersistInfo {
258   lua_Writer writer;
259   void *ud;
260   const char *metafield;
261   bool writeDebugInfo;
262 } PersistInfo;
263 
264 /* State information when unpersisting an object. */
265 typedef struct UnpersistInfo {
266   ZIO zio;
267   size_t sizeof_int;
268   size_t sizeof_size_t;
269 } UnpersistInfo;
270 
271 /* Info shared in persist and unpersist. */
272 typedef struct Info {
273   lua_State *L;
274   lua_Unsigned level;
275   int refcount; /* int because rawseti/rawgeti takes an int. */
276   lua_Unsigned maxComplexity;
277   bool generatePath;
278   bool passIOToPersist;
279   /* Which one it really is will always be clear from the context. */
280   union {
281     PersistInfo pi;
282     UnpersistInfo upi;
283   } u;
284 } Info;
285 
286 /* Type names, used for error messages. */
287 static const char *const kTypenames[] = {
288   "nil", "boolean", "lightuserdata", "number", "string",
289   "table", "function", "userdata", "thread", "proto", "upval",
290   "deadkey", "permanent"
291 };
292 
293 /* Setting names as used in eris.settings / eris_g|set_setting. Also, the
294  * addresses of these static variables are used as keys in the registry of Lua
295  * states to save the current values of the settings (as light userdata). */
296 static const char *const kSettingMetafield = "spkey";
297 static const char *const kSettingPassIOToPersist = "spio";
298 static const char *const kSettingGeneratePath = "path";
299 static const char *const kSettingWriteDebugInfo = "debug";
300 static const char *const kSettingMaxComplexity = "maxrec";
301 
302 /* Header we prefix to persisted data for a quick check when unpersisting. */
303 static char const kHeader[] = { 'E', 'R', 'I', 'S' };
304 #define HEADER_LENGTH sizeof(kHeader)
305 
306 /* Floating point number used to check compatibility of loaded data. */
307 static const lua_Number kHeaderNumber = (lua_Number)-1.234567890;
308 
309 /* Stack indices of some internal values/tables, to avoid magic numbers. */
310 #define PERMIDX 1
311 #define REFTIDX 2
312 #define BUFFIDX 3
313 #define PATHIDX 4
314 
315 /* Table indices for upvalue tables, keeping track of upvals to open. */
316 #define UVTOCL 1
317 #define UVTONU 2
318 #define UVTVAL 3
319 #define UVTREF 4
320 
321 /* }======================================================================== */
322 
323 /*
324 ** {===========================================================================
325 ** Utility functions.
326 ** ============================================================================
327 */
328 
329 /* Pushes an object into the reference table when unpersisting. This creates an
330  * entry pointing from the id the object is referenced by to the object. */
331 static int
registerobject(Info * info)332 registerobject(Info *info) {                          /* perms reftbl ... obj */
333   const int reference = ++(info->refcount);
334   eris_checkstack(info->L, 1);
335   lua_pushvalue(info->L, -1);                     /* perms reftbl ... obj obj */
336   lua_rawseti(info->L, REFTIDX, reference);           /* perms reftbl ... obj */
337   return reference;
338 }
339 
340 /** ======================================================================== */
341 
342 /* Pushes a TString* onto the stack if it holds a value, nil if it is NULL. */
343 static void
pushtstring(lua_State * L,TString * ts)344 pushtstring(lua_State* L, TString *ts) {                               /* ... */
345   if (ts) {
346     eris_setsvalue2n(L, L->top, ts);
347     api_incr_top(L);                                              /* ... str */
348   }
349   else {
350     lua_pushnil(L);                                                /* ... nil */
351   }
352 }
353 
354 /* Creates a copy of the string on top of the stack and sets it as the value
355  * of the specified TString**. */
356 static void
copytstring(lua_State * L,TString ** ts)357 copytstring(lua_State* L, TString **ts) {
358   size_t length;
359   const char *value = lua_tolstring(L, -1, &length);
360   *ts = eris_newlstr(L, value, length);
361 }
362 
363 /** ======================================================================== */
364 
365 /* Pushes the specified segment to the current path, if we're generating one.
366  * This supports formatting strings using Lua's formatting capabilities. */
367 static void
pushpath(Info * info,const char * fmt,...)368 pushpath(Info *info, const char* fmt, ...) {     /* perms reftbl var path ... */
369   if (!info->generatePath) {
370     return;
371   }
372   else {
373     va_list argp;
374     eris_checkstack(info->L, 1);
375     va_start(argp, fmt);
376     lua_pushvfstring(info->L, fmt, argp);    /* perms reftbl var path ... str */
377     va_end(argp);
378     lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX) + 1);
379   }                                              /* perms reftbl var path ... */
380 }
381 
382 /* Pops the last added segment from the current path if we're generating one. */
383 static void
poppath(Info * info)384 poppath(Info *info) {                            /* perms reftbl var path ... */
385   if (!info->generatePath) {
386     return;
387   }
388   eris_checkstack(info->L, 1);
389   lua_pushnil(info->L);                      /* perms reftbl var path ... nil */
390   lua_rawseti(info->L, PATHIDX, luaL_len(info->L, PATHIDX));
391 }                                                /* perms reftbl var path ... */
392 
393 /* Concatenates all current path segments into one string, pushes it and
394  * returns it. This is relatively inefficient, but it's for errors only and
395  * keeps the stack small, so it's better this way. */
396 static const char*
path(Info * info)397 path(Info *info) {                               /* perms reftbl var path ... */
398   if (!info->generatePath) {
399     return "";
400   }
401   eris_checkstack(info->L, 3);
402   lua_pushstring(info->L, "");               /* perms reftbl var path ... str */
403   lua_pushnil(info->L);                  /* perms reftbl var path ... str nil */
404   while (lua_next(info->L, PATHIDX)) {   /* perms reftbl var path ... str k v */
405     lua_insert(info->L, -2);             /* perms reftbl var path ... str v k */
406     lua_insert(info->L, -3);             /* perms reftbl var path ... k str v */
407     lua_concat(info->L, 2);                /* perms reftbl var path ... k str */
408     lua_insert(info->L, -2);               /* perms reftbl var path ... str k */
409   }                                          /* perms reftbl var path ... str */
410   return lua_tostring(info->L, -1);
411 }
412 
413 /* Generates an error message with the appended path, if available. */
414 static int
eris_error(Info * info,const char * fmt,...)415 eris_error(Info *info, const char *fmt, ...) {                         /* ... */
416     va_list argp;
417     eris_checkstack(info->L, 5);
418 
419     luaL_where(info->L, 1);                                     /* ... where */
420     va_start(argp, fmt);
421     lua_pushvfstring(info->L, fmt, argp);                    /* ... where str */
422     va_end(argp);
423     if (info->generatePath) {
424       lua_pushstring(info->L, " (");                    /* ... where str " (" */
425       path(info);                                 /* ...  where str " (" path */
426       lua_pushstring(info->L, ")");            /* ... where str " (" path ")" */
427       lua_concat(info->L, 5);                                      /* ... msg */
428     }
429     else {
430       lua_concat(info->L, 2);                                      /* ... msg */
431     }
432     return lua_error(info->L);
433 }
434 
435 /** ======================================================================== */
436 
437 /* Tries to get a setting from the registry. */
438 static bool
get_setting(lua_State * L,void * key)439 get_setting(lua_State *L, void *key) {                                 /* ... */
440   eris_checkstack(L, 1);
441   lua_pushlightuserdata(L, key);                                   /* ... key */
442   lua_gettable(L, LUA_REGISTRYINDEX);                        /* ... value/nil */
443   if (lua_isnil(L, -1)) {                                          /* ... nil */
444     lua_pop(L, 1);                                                     /* ... */
445     return false;
446   }                                                              /* ... value */
447   return true;
448 }
449 
450 /* Stores a setting in the registry (or removes it if the value is nil). */
451 static void
set_setting(lua_State * L,void * key)452 set_setting(lua_State *L, void *key) {                           /* ... value */
453   eris_checkstack(L, 2);
454   lua_pushlightuserdata(L, key);                             /* ... value key */
455   lua_insert(L, -2);                                         /* ... key value */
456   lua_settable(L, LUA_REGISTRYINDEX);                                  /* ... */
457 }
458 
459 /* Used as a callback for luaL_opt to check boolean setting values. */
460 static bool
checkboolean(lua_State * L,int narg)461 checkboolean(lua_State *L, int narg) {                       /* ... bool? ... */
462   if (!lua_isboolean(L, narg)) {                                /* ... :( ... */
463     return luaL_argerror(L, narg, lua_pushfstring(L,
464       "boolean expected, got %s", lua_typename(L, lua_type(L, narg))));
465   }                                                           /* ... bool ... */
466   return lua_toboolean(L, narg);
467 }
468 
469 /* }======================================================================== */
470 
471 /*
472 ** {===========================================================================
473 ** Persist and unpersist.
474 ** ============================================================================
475 */
476 
477 /* I have macros and I'm not afraid to use them! These are highly situational
478  * and assume an Info* named 'info' is available. */
479 
480 /* Writes a raw memory block with the specified size. */
481 #define WRITE_RAW(value, size) {\
482   if (info->u.pi.writer(info->L, (value), (size), info->u.pi.ud)) \
483     eris_error(info, ERIS_ERR_WRITE); }
484 
485 /* Writes a single value with the specified type. */
486 #define WRITE_VALUE(value, type) write_##type(info, value)
487 
488 /* Writes a typed array with the specified length. */
489 #define WRITE(value, length, type) { \
490     int i; for (i = 0; i < length; ++i) WRITE_VALUE((value)[i], type); }
491 
492 /** ======================================================================== */
493 
494 /* Reads a raw block of memory with the specified size. */
495 #define READ_RAW(value, size) {\
496   if (eris_read(&info->u.upi.zio, (value), (size))) \
497     eris_error(info, ERIS_ERR_READ); }
498 
499 /* Reads a single value with the specified type. */
500 #define READ_VALUE(type) read_##type(info)
501 
502 /* Reads a typed array with the specified length. */
503 #define READ(value, length, type) { \
504     int i; for (i = 0; i < length; ++i) (value)[i] = READ_VALUE(type); }
505 
506 /** ======================================================================== */
507 
508 static void
write_uint8_t(Info * info,uint8_t value)509 write_uint8_t(Info *info, uint8_t value) {
510   WRITE_RAW(&value, sizeof(uint8_t));
511 }
512 
513 static void
write_uint16_t(Info * info,uint16_t value)514 write_uint16_t(Info *info, uint16_t value) {
515   write_uint8_t(info, value);
516   write_uint8_t(info, value >> 8);
517 }
518 
519 static void
write_uint32_t(Info * info,uint32_t value)520 write_uint32_t(Info *info, uint32_t value) {
521   write_uint8_t(info, value);
522   write_uint8_t(info, value >> 8);
523   write_uint8_t(info, value >> 16);
524   write_uint8_t(info, value >> 24);
525 }
526 
527 static void
write_uint64_t(Info * info,uint64_t value)528 write_uint64_t(Info *info, uint64_t value) {
529   write_uint8_t(info, value);
530   write_uint8_t(info, value >> 8);
531   write_uint8_t(info, value >> 16);
532   write_uint8_t(info, value >> 24);
533   write_uint8_t(info, value >> 32);
534   write_uint8_t(info, value >> 40);
535   write_uint8_t(info, value >> 48);
536   write_uint8_t(info, value >> 56);
537 }
538 
539 static void
write_int16_t(Info * info,int16_t value)540 write_int16_t(Info *info, int16_t value) {
541   write_uint16_t(info, (uint16_t)value);
542 }
543 
544 static void
write_int32_t(Info * info,int32_t value)545 write_int32_t(Info *info, int32_t value) {
546   write_uint32_t(info, (uint32_t)value);
547 }
548 
549 static void
write_int64_t(Info * info,int64_t value)550 write_int64_t(Info *info, int64_t value) {
551   write_uint64_t(info, (uint64_t)value);
552 }
553 
554 static void
write_float32(Info * info,float value)555 write_float32(Info *info, float value) {
556   uint32_t rep;
557   memcpy(&rep, &value, sizeof(float));
558   write_uint32_t(info, rep);
559 }
560 
561 static void
write_float64(Info * info,double value)562 write_float64(Info *info, double value) {
563   uint64_t rep;
564   memcpy(&rep, &value, sizeof(double));
565   write_uint64_t(info, rep);
566 }
567 
568 /* Note regarding the following: any decent compiler should be able
569  * to reduce these to just the write call, since sizeof is constant. */
570 
571 static void
write_int(Info * info,int value)572 write_int(Info *info, int value) {
573   if (sizeof(int) == sizeof(int16_t)) {
574     write_int16_t(info, value);
575   }
576   else if (sizeof(int) == sizeof(int32_t)) {
577     write_int32_t(info, value);
578   }
579   else if (sizeof(int) == sizeof(int64_t)) {
580     write_int64_t(info, value);
581   }
582   else {
583     eris_error(info, ERIS_ERR_TYPE_INT);
584   }
585 }
586 
587 static void
write_size_t(Info * info,size_t value)588 write_size_t(Info *info, size_t value) {
589   if (sizeof(size_t) == sizeof(uint16_t)) {
590     write_uint16_t(info, value);
591   }
592   else if (sizeof(size_t) == sizeof(uint32_t)) {
593     write_uint32_t(info, value);
594   }
595   else if (sizeof(size_t) == sizeof(uint64_t)) {
596     write_uint64_t(info, value);
597   }
598   else {
599     eris_error(info, ERIS_ERR_TYPE_SIZE);
600   }
601 }
602 
603 static void
write_lua_Number(Info * info,lua_Number value)604 write_lua_Number(Info *info, lua_Number value) {
605   if (sizeof(lua_Number) == sizeof(uint32_t)) {
606     write_float32(info, value);
607   }
608   else if (sizeof(lua_Number) == sizeof(uint64_t)) {
609     write_float64(info, value);
610   }
611   else {
612     eris_error(info, ERIS_ERR_TYPE_FLOAT);
613   }
614 }
615 
616 static void
write_lua_Integer(Info * info,lua_Integer value)617 write_lua_Integer(Info *info, lua_Integer value) {
618   if (sizeof(lua_Integer) == sizeof(uint32_t)) {
619     write_uint32_t(info, value);
620   }
621   else if (sizeof(lua_Integer) == sizeof(uint64_t)) {
622     write_uint64_t(info, value);
623   }
624   else {
625     eris_error(info, ERIS_ERR_TYPE_INT);
626   }
627 }
628 
629 /* Note that Lua only ever uses 32 bits of the Instruction type, so we can
630  * assert that there will be no truncation, even if the underlying type has
631  * more bits (might be the case on some 64 bit systems). */
632 
633 static void
write_Instruction(Info * info,Instruction value)634 write_Instruction(Info *info, Instruction value) {
635   if (sizeof(Instruction) == sizeof(uint32_t)) {
636     write_uint32_t(info, value);
637   }
638   else {
639     uint32_t pvalue = (uint32_t)value;
640     /* Lua only uses 32 bits for its instructions. */
641     eris_assert((Instruction)pvalue == value);
642     write_uint32_t(info, pvalue);
643   }
644 }
645 
646 /** ======================================================================== */
647 
648 static uint8_t
read_uint8_t(Info * info)649 read_uint8_t(Info *info) {
650   uint8_t value;
651   READ_RAW(&value, sizeof(uint8_t));
652   return value;
653 }
654 
655 static uint16_t
read_uint16_t(Info * info)656 read_uint16_t(Info *info) {
657   return  (uint16_t)read_uint8_t(info) |
658          ((uint16_t)read_uint8_t(info) << 8);
659 }
660 
661 static uint32_t
read_uint32_t(Info * info)662 read_uint32_t(Info *info) {
663   return  (uint32_t)read_uint8_t(info) |
664          ((uint32_t)read_uint8_t(info) << 8) |
665          ((uint32_t)read_uint8_t(info) << 16) |
666          ((uint32_t)read_uint8_t(info) << 24);
667 }
668 
669 static uint64_t
read_uint64_t(Info * info)670 read_uint64_t(Info *info) {
671   return  (uint64_t)read_uint8_t(info) |
672          ((uint64_t)read_uint8_t(info) << 8) |
673          ((uint64_t)read_uint8_t(info) << 16) |
674          ((uint64_t)read_uint8_t(info) << 24) |
675          ((uint64_t)read_uint8_t(info) << 32) |
676          ((uint64_t)read_uint8_t(info) << 40) |
677          ((uint64_t)read_uint8_t(info) << 48) |
678          ((uint64_t)read_uint8_t(info) << 56);
679 }
680 
681 static int16_t
read_int16_t(Info * info)682 read_int16_t(Info *info) {
683   return (int16_t)read_uint16_t(info);
684 }
685 
686 static int32_t
read_int32_t(Info * info)687 read_int32_t(Info *info) {
688   return (int32_t)read_uint32_t(info);
689 }
690 
691 static int64_t
read_int64_t(Info * info)692 read_int64_t(Info *info) {
693   return (int64_t)read_uint64_t(info);
694 }
695 
696 static float
read_float32(Info * info)697 read_float32(Info *info) {
698   float value;
699   uint32_t rep = read_uint32_t(info);
700   memcpy(&value, &rep, sizeof(float));
701   return value;
702 }
703 
704 static double
read_float64(Info * info)705 read_float64(Info *info) {
706   double value;
707   uint64_t rep = read_uint64_t(info);
708   memcpy(&value, &rep, sizeof(double));
709   return value;
710 }
711 
712 /* Note regarding the following: unlike with writing the sizeof check will be
713  * impossible to optimize away, since it depends on the input; however, the
714  * truncation check may be optimized away in the case where the read data size
715  * equals the native one, so reading data written on the same machine should be
716  * reasonably quick. Doing a (rather rudimentary) benchmark this did not have
717  * any measurable impact on performance. */
718 
719 static int
read_int(Info * info)720 read_int(Info *info) {
721   int value;
722   if (info->u.upi.sizeof_int == sizeof(int16_t)) {
723     int16_t pvalue = read_int16_t(info);
724     value = (int)pvalue;
725     if ((int32_t)value != pvalue) {
726       eris_error(info, ERIS_ERR_TRUNC_INT);
727     }
728   }
729   else if (info->u.upi.sizeof_int == sizeof(int32_t)) {
730     int32_t pvalue = read_int32_t(info);
731     value = (int)pvalue;
732     if ((int32_t)value != pvalue) {
733       eris_error(info, ERIS_ERR_TRUNC_INT);
734     }
735   }
736   else if (info->u.upi.sizeof_int == sizeof(int64_t)) {
737     int64_t pvalue = read_int64_t(info);
738     value = (int)pvalue;
739     if ((int64_t)value != pvalue) {
740       eris_error(info, ERIS_ERR_TRUNC_INT);
741     }
742   }
743   else {
744     eris_error(info, ERIS_ERR_TYPE_INT);
745     value = 0; /* not reached */
746   }
747   return value;
748 }
749 
750 static size_t
read_size_t(Info * info)751 read_size_t(Info *info) {
752   size_t value;
753   if (info->u.upi.sizeof_size_t == sizeof(uint16_t)) {
754     uint16_t pvalue = read_uint16_t(info);
755     value = (size_t)pvalue;
756     if ((uint32_t)value != pvalue) {
757       eris_error(info, ERIS_ERR_TRUNC_SIZE);
758     }
759   }
760   else if (info->u.upi.sizeof_size_t == sizeof(uint32_t)) {
761     uint32_t pvalue = read_uint32_t(info);
762     value = (size_t)pvalue;
763     if ((uint32_t)value != pvalue) {
764       eris_error(info, ERIS_ERR_TRUNC_SIZE);
765     }
766   }
767   else if (info->u.upi.sizeof_size_t == sizeof(uint64_t)) {
768     uint64_t pvalue = read_uint64_t(info);
769     value = (size_t)pvalue;
770     if ((uint64_t)value != pvalue) {
771       eris_error(info, ERIS_ERR_TRUNC_SIZE);
772     }
773   }
774   else {
775     eris_error(info, ERIS_ERR_TYPE_SIZE);
776     value = 0; /* not reached */
777   }
778   return value;
779 }
780 
781 static lua_Number
read_lua_Number(Info * info)782 read_lua_Number(Info *info) {
783   if (sizeof(lua_Number) == sizeof(uint32_t)) {
784     return read_float32(info);
785   }
786   else if (sizeof(lua_Number) == sizeof(uint64_t)) {
787     return read_float64(info);
788   }
789   else {
790     eris_error(info, ERIS_ERR_TYPE_FLOAT);
791     return 0; /* not reached */
792   }
793 }
794 
795 static lua_Integer
read_lua_Integer(Info * info)796 read_lua_Integer(Info *info) {
797   if (sizeof(lua_Integer) == sizeof(uint32_t)) {
798     return (lua_Integer)read_uint32_t(info);
799   }
800   else if (sizeof(lua_Integer) == sizeof(uint64_t)) {
801     return (lua_Integer)read_uint64_t(info);
802   }
803   else {
804     eris_error(info, ERIS_ERR_TYPE_INT);
805     return 0; /* not reached */
806   }
807 }
808 
809 static Instruction
read_Instruction(Info * info)810 read_Instruction(Info *info) {
811   return (Instruction)read_uint32_t(info);
812 }
813 
814 /** ======================================================================== */
815 
816 /* Forward declarations for recursively called top-level functions. */
817 static void persist_keyed(Info*, int type);
818 static void persist(Info*);
819 static void unpersist(Info*);
820 
821 /*
822 ** ============================================================================
823 ** Simple types.
824 ** ============================================================================
825 */
826 
827 static void
p_boolean(Info * info)828 p_boolean(Info *info) {                                           /* ... bool */
829   WRITE_VALUE(lua_toboolean(info->L, -1), uint8_t);
830 }
831 
832 static void
u_boolean(Info * info)833 u_boolean(Info *info) {                                                /* ... */
834   eris_checkstack(info->L, 1);
835   lua_pushboolean(info->L, READ_VALUE(uint8_t));                  /* ... bool */
836 
837   eris_assert(lua_type(info->L, -1) == LUA_TBOOLEAN);
838 }
839 
840 /** ======================================================================== */
841 
842 static void
p_pointer(Info * info)843 p_pointer(Info *info) {                                         /* ... ludata */
844   WRITE_VALUE((size_t)lua_touserdata(info->L, -1), size_t);
845 }
846 
847 static void
u_pointer(Info * info)848 u_pointer(Info *info) {                                                /* ... */
849   eris_checkstack(info->L, 1);
850   lua_pushlightuserdata(info->L, (void*)READ_VALUE(size_t));    /* ... ludata */
851 
852   eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
853 }
854 
855 /** ======================================================================== */
856 
857 static void
p_number(Info * info)858 p_number(Info *info) {                                             /* ... num */
859   if (lua_isinteger(info->L, -1)) {
860     WRITE_VALUE(true, uint8_t);
861     WRITE_VALUE(lua_tointeger(info->L, -1), lua_Integer);
862   }
863   else {
864     WRITE_VALUE(false, uint8_t);
865     WRITE_VALUE(lua_tonumber(info->L, -1), lua_Number);
866   }
867 }
868 
869 static void
u_number(Info * info)870 u_number(Info *info) {                                                 /* ... */
871   eris_checkstack(info->L, 1);
872   if (READ_VALUE(uint8_t)) {
873     lua_pushinteger(info->L, READ_VALUE(lua_Integer));
874   }
875   else {
876     lua_pushnumber(info->L, READ_VALUE(lua_Number));               /* ... num */
877   }
878 
879   eris_assert(lua_type(info->L, -1) == LUA_TNUMBER);
880 }
881 
882 /** ======================================================================== */
883 
884 static void
p_string(Info * info)885 p_string(Info *info) {                                             /* ... str */
886   size_t length;
887   const char *value = lua_tolstring(info->L, -1, &length);
888   WRITE_VALUE(length, size_t);
889   WRITE_RAW(value, length);
890 }
891 
892 static void
u_string(Info * info)893 u_string(Info *info) {                                                 /* ... */
894   eris_checkstack(info->L, 2);
895   {
896     /* TODO Can we avoid this copy somehow? (Without it getting too nasty) */
897     const size_t length = READ_VALUE(size_t);
898     char *value = (char*)lua_newuserdata(info->L, length * sizeof(char)); /* ... tmp */
899     READ_RAW(value, length);
900     lua_pushlstring(info->L, value, length);                   /* ... tmp str */
901     lua_replace(info->L, -2);                                      /* ... str */
902   }
903   registerobject(info);
904 
905   eris_assert(lua_type(info->L, -1) == LUA_TSTRING);
906 }
907 
908 /*
909 ** ============================================================================
910 ** Tables and userdata.
911 ** ============================================================================
912 */
913 
914 static void
p_metatable(Info * info)915 p_metatable(Info *info) {                                          /* ... obj */
916   eris_checkstack(info->L, 1);
917   pushpath(info, "@metatable");
918   if (!lua_getmetatable(info->L, -1)) {                        /* ... obj mt? */
919     lua_pushnil(info->L);                                      /* ... obj nil */
920   }                                                         /* ... obj mt/nil */
921   persist(info);                                            /* ... obj mt/nil */
922   lua_pop(info->L, 1);                                             /* ... obj */
923   poppath(info);
924 }
925 
926 static void
u_metatable(Info * info)927 u_metatable(Info *info) {                                          /* ... tbl */
928   eris_checkstack(info->L, 1);
929   pushpath(info, "@metatable");
930   unpersist(info);                                         /* ... tbl mt/nil? */
931   if (lua_istable(info->L, -1)) {                               /* ... tbl mt */
932     lua_setmetatable(info->L, -2);                                 /* ... tbl */
933   }
934   else if (lua_isnil(info->L, -1)) {                           /* ... tbl nil */
935     lua_pop(info->L, 1);                                           /* ... tbl */
936   }
937   else {                                                            /* tbl :( */
938     eris_error(info, ERIS_ERR_METATABLE);
939   }
940   poppath(info);
941 }
942 
943 /** ======================================================================== */
944 
945 static void
p_literaltable(Info * info)946 p_literaltable(Info *info) {                                       /* ... tbl */
947   eris_checkstack(info->L, 3);
948 
949   /* Persist all key / value pairs. */
950   lua_pushnil(info->L);                                        /* ... tbl nil */
951   while (lua_next(info->L, -2)) {                              /* ... tbl k v */
952     lua_pushvalue(info->L, -2);                              /* ... tbl k v k */
953 
954     if (info->generatePath) {
955       if (lua_type(info->L, -1) == LUA_TSTRING) {
956         const char *key = lua_tostring(info->L, -1);
957         pushpath(info, ".%s", key);
958       }
959       else {
960         const char *key = luaL_tolstring(info->L, -1, NULL);
961         pushpath(info, "[%s]", key);
962         lua_pop(info->L, 1);
963       }
964     }
965 
966     persist(info);                                           /* ... tbl k v k */
967     lua_pop(info->L, 1);                                       /* ... tbl k v */
968     persist(info);                                             /* ... tbl k v */
969     lua_pop(info->L, 1);                                         /* ... tbl k */
970 
971     poppath(info);
972   }                                                                /* ... tbl */
973 
974   /* Terminate list. */
975   lua_pushnil(info->L);                                        /* ... tbl nil */
976   persist(info);                                               /* ... tbl nil */
977   lua_pop(info->L, 1);                                             /* ... tbl */
978 
979   p_metatable(info);
980 }
981 
982 static void
u_literaltable(Info * info)983 u_literaltable(Info *info) {                                           /* ... */
984   eris_checkstack(info->L, 3);
985 
986   lua_newtable(info->L);                                           /* ... tbl */
987 
988   /* Preregister table for handling of cycles (keys, values or metatable). */
989   registerobject(info);
990 
991   /* Unpersist all key / value pairs. */
992   for (;;) {
993     pushpath(info, "@key");
994     unpersist(info);                                       /* ... tbl key/nil */
995     poppath(info);
996     if (lua_isnil(info->L, -1)) {                              /* ... tbl nil */
997       lua_pop(info->L, 1);                                         /* ... tbl */
998       break;
999     }                                                          /* ... tbl key */
1000 
1001     if (info->generatePath) {
1002       if (lua_type(info->L, -1) == LUA_TSTRING) {
1003         const char *key = lua_tostring(info->L, -1);
1004         pushpath(info, ".%s", key);
1005       }
1006       else {
1007         const char *key = luaL_tolstring(info->L, -1, NULL);
1008         pushpath(info, "[%s]", key);
1009         lua_pop(info->L, 1);
1010       }
1011     }
1012 
1013     unpersist(info);                                    /* ... tbl key value? */
1014     if (!lua_isnil(info->L, -1)) {                       /* ... tbl key value */
1015       lua_rawset(info->L, -3);                                     /* ... tbl */
1016     }
1017     else {
1018       eris_error(info, ERIS_ERR_TABLE);
1019     }
1020 
1021     poppath(info);
1022   }
1023 
1024   u_metatable(info);                                               /* ... tbl */
1025 }
1026 
1027 /** ======================================================================== */
1028 
1029 static void
p_literaluserdata(Info * info)1030 p_literaluserdata(Info *info) {                                  /* ... udata */
1031   const size_t size = lua_rawlen(info->L, -1);
1032   const void *value = lua_touserdata(info->L, -1);
1033   WRITE_VALUE(size, size_t);
1034   WRITE_RAW(value, size);
1035   p_metatable(info);                                             /* ... udata */
1036 }
1037 
1038 static void
u_literaluserdata(Info * info)1039 u_literaluserdata(Info *info) {                                        /* ... */
1040   eris_checkstack(info->L, 1);
1041   {
1042     size_t size = READ_VALUE(size_t);
1043     void *value = lua_newuserdata(info->L, size);                /* ... udata */
1044     READ_RAW(value, size);                                       /* ... udata */
1045   }
1046   registerobject(info);
1047   u_metatable(info);
1048 }
1049 
1050 /** ======================================================================== */
1051 
1052 typedef void (*Callback) (Info*);
1053 
1054 static void
p_special(Info * info,Callback literal)1055 p_special(Info *info, Callback literal) {                          /* ... obj */
1056   int allow = (lua_type(info->L, -1) == LUA_TTABLE);
1057   eris_checkstack(info->L, 4);
1058 
1059   /* Check whether we should persist literally, or via the metafunction. */
1060   if (lua_getmetatable(info->L, -1)) {                          /* ... obj mt */
1061     lua_pushstring(info->L, info->u.pi.metafield);         /* ... obj mt pkey */
1062     lua_rawget(info->L, -2);                           /* ... obj mt persist? */
1063     switch (lua_type(info->L, -1)) {
1064       /* No entry, act according to default. */
1065       case LUA_TNIL:                                        /* ... obj mt nil */
1066         lua_pop(info->L, 2);                                       /* ... obj */
1067         break;
1068 
1069       /* Boolean value, tells us whether allowed or not. */
1070       case LUA_TBOOLEAN:                                   /* ... obj mt bool */
1071         allow = lua_toboolean(info->L, -1);
1072         lua_pop(info->L, 2);                                       /* ... obj */
1073         break;
1074 
1075       /* Function value, call it and don't persist literally. */
1076       case LUA_TFUNCTION:                                  /* ... obj mt func */
1077         lua_replace(info->L, -2);                             /* ... obj func */
1078         lua_pushvalue(info->L, -2);                       /* ... obj func obj */
1079 
1080         if (info->passIOToPersist) {
1081           lua_pushlightuserdata(info->L, (void*)info->u.pi.writer);
1082                                                    /* ... obj func obj writer */
1083           lua_pushlightuserdata(info->L, info->u.pi.ud);
1084                                                 /* ... obj func obj writer ud */
1085           lua_call(info->L, 3, 1);                           /* ... obj func? */
1086         }
1087         else {
1088           lua_call(info->L, 1, 1);                           /* ... obj func? */
1089         }
1090         if (!lua_isfunction(info->L, -1)) {                     /* ... obj :( */
1091           eris_error(info, ERIS_ERR_SPER_FUNC, info->u.pi.metafield);
1092         }                                                     /* ... obj func */
1093 
1094         /* Special persistence, call this function when unpersisting. */
1095         WRITE_VALUE(true, uint8_t);
1096         persist(info);                                        /* ... obj func */
1097         lua_pop(info->L, 1);                                       /* ... obj */
1098         return;
1099       default:                                               /* ... obj mt :( */
1100         eris_error(info, ERIS_ERR_SPER_TYPE, info->u.pi.metafield);
1101         return; /* not reached */
1102     }
1103   }
1104 
1105   if (allow) {
1106     /* Not special but literally persisted object. */
1107     WRITE_VALUE(false, uint8_t);
1108     literal(info);                                                 /* ... obj */
1109   }
1110   else if (lua_type(info->L, -1) == LUA_TTABLE) {
1111     eris_error(info, ERIS_ERR_SPER_PROT);
1112   }
1113   else {
1114     eris_error(info, ERIS_ERR_USERDATA);
1115   }
1116 }
1117 
1118 static void
u_special(Info * info,int type,Callback literal)1119 u_special(Info *info, int type, Callback literal) {                    /* ... */
1120   eris_checkstack(info->L, 2);
1121   if (READ_VALUE(uint8_t)) {
1122     int reference;
1123     /* Reserve entry in the reftable before unpersisting the function to keep
1124      * the reference order intact. We can set this to nil at first, because
1125      * there's no way the special function would access this. */
1126     lua_pushnil(info->L);                                          /* ... nil */
1127     reference = registerobject(info);
1128     lua_pop(info->L, 1);                                               /* ... */
1129     /* Increment reference counter by one to compensate for the increment when
1130      * persisting a special object. */
1131     unpersist(info);                                           /* ... spfunc? */
1132     if (!lua_isfunction(info->L, -1)) {                             /* ... :( */
1133       eris_error(info, ERIS_ERR_SPER_UFUNC);
1134     }                                                           /* ... spfunc */
1135 
1136     if (info->passIOToPersist) {
1137       lua_pushlightuserdata(info->L, &info->u.upi.zio);     /* ... spfunc zio */
1138       lua_call(info->L, 1, 1);                                    /* ... obj? */
1139     } else {
1140       lua_call(info->L, 0, 1);                                    /* ... obj? */
1141     }
1142 
1143     if (lua_type(info->L, -1) != type) {                            /* ... :( */
1144       const char *want = kTypenames[type];
1145       const char *have = kTypenames[lua_type(info->L, -1)];
1146       eris_error(info, ERIS_ERR_SPER_LOAD, want, have);
1147     }                                                              /* ... obj */
1148 
1149     /* Update the reftable entry. */
1150     lua_pushvalue(info->L, -1);                                /* ... obj obj */
1151     lua_rawseti(info->L, 2, reference);                            /* ... obj */
1152   }
1153   else {
1154     literal(info);                                                 /* ... obj */
1155   }
1156 }
1157 
1158 /** ======================================================================== */
1159 
1160 static void
p_table(Info * info)1161 p_table(Info *info) {                                              /* ... tbl */
1162   p_special(info, p_literaltable);                                 /* ... tbl */
1163 }
1164 
1165 static void
u_table(Info * info)1166 u_table(Info *info) {                                                  /* ... */
1167   u_special(info, LUA_TTABLE, u_literaltable);                     /* ... tbl */
1168 
1169   eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1170 }
1171 
1172 /** ======================================================================== */
1173 
1174 static void
p_userdata(Info * info)1175 p_userdata(Info *info) {                            /* perms reftbl ... udata */
1176   p_special(info, p_literaluserdata);
1177 }
1178 
1179 static void
u_userdata(Info * info)1180 u_userdata(Info *info) {                                               /* ... */
1181   u_special(info, LUA_TUSERDATA, u_literaluserdata);             /* ... udata */
1182 
1183   eris_assert(lua_type(info->L, -1) == LUA_TUSERDATA);
1184 }
1185 
1186 /*
1187 ** ============================================================================
1188 ** Closures and threads.
1189 ** ============================================================================
1190 */
1191 
1192 /* We track the actual upvalues themselves by pushing their "id" (meaning a
1193  * pointer to them) as lightuserdata to the reftable. This is safe because
1194  * lightuserdata will not normally end up in there, because simple value types
1195  * are always persisted directly (because that'll be just as large, memory-
1196  * wise as when pointing to the first instance). Same for protos. */
1197 
1198 static void
p_proto(Info * info)1199 p_proto(Info *info) {                                            /* ... proto */
1200   int i;
1201   const Proto *p = (Proto*)lua_touserdata(info->L, -1);
1202   eris_checkstack(info->L, 3);
1203 
1204   /* Write general information. */
1205   WRITE_VALUE(p->linedefined, int);
1206   WRITE_VALUE(p->lastlinedefined, int);
1207   WRITE_VALUE(p->numparams, uint8_t);
1208   WRITE_VALUE(p->is_vararg, uint8_t);
1209   WRITE_VALUE(p->maxstacksize, uint8_t);
1210 
1211   /* Write byte code. */
1212   WRITE_VALUE(p->sizecode, int);
1213   WRITE(p->code, p->sizecode, Instruction);
1214 
1215   /* Write constants. */
1216   WRITE_VALUE(p->sizek, int);
1217   pushpath(info, ".constants");
1218   for (i = 0; i < p->sizek; ++i) {
1219     pushpath(info, "[%d]", i);
1220     eris_setobj(info->L, info->L->top++, &p->k[i]);      /* ... lcl proto obj */
1221     persist(info);                                       /* ... lcl proto obj */
1222     lua_pop(info->L, 1);                                     /* ... lcl proto */
1223     poppath(info);
1224   }
1225   poppath(info);
1226 
1227   /* Write child protos. */
1228   WRITE_VALUE(p->sizep, int);
1229   pushpath(info, ".protos");
1230   for (i = 0; i < p->sizep; ++i) {
1231     pushpath(info, "[%d]", i);
1232     lua_pushlightuserdata(info->L, p->p[i]);           /* ... lcl proto proto */
1233     lua_pushvalue(info->L, -1);                  /* ... lcl proto proto proto */
1234     persist_keyed(info, LUA_TPROTO);                   /* ... lcl proto proto */
1235     lua_pop(info->L, 1);                                     /* ... lcl proto */
1236     poppath(info);
1237   }
1238   poppath(info);
1239 
1240   /* Write upvalues. */
1241   WRITE_VALUE(p->sizeupvalues, int);
1242   for (i = 0; i < p->sizeupvalues; ++i) {
1243     WRITE_VALUE(p->upvalues[i].instack, uint8_t);
1244     WRITE_VALUE(p->upvalues[i].idx, uint8_t);
1245   }
1246 
1247   /* If we don't have to persist debug information skip the rest. */
1248   WRITE_VALUE(info->u.pi.writeDebugInfo, uint8_t);
1249   if (!info->u.pi.writeDebugInfo) {
1250     return;
1251   }
1252 
1253   /* Write function source code. */
1254   pushtstring(info->L, p->source);                    /* ... lcl proto source */
1255   persist(info);                                      /* ... lcl proto source */
1256   lua_pop(info->L, 1);                                       /* ... lcl proto */
1257 
1258   /* Write line information. */
1259   WRITE_VALUE(p->sizelineinfo, int);
1260   WRITE(p->lineinfo, p->sizelineinfo, int);
1261 
1262   /* Write locals info. */
1263   WRITE_VALUE(p->sizelocvars, int);
1264   pushpath(info, ".locvars");
1265   for (i = 0; i < p->sizelocvars; ++i) {
1266     pushpath(info, "[%d]", i);
1267     WRITE_VALUE(p->locvars[i].startpc, int);
1268     WRITE_VALUE(p->locvars[i].endpc, int);
1269     pushtstring(info->L, p->locvars[i].varname);     /* ... lcl proto varname */
1270     persist(info);                                   /* ... lcl proto varname */
1271     lua_pop(info->L, 1);                                     /* ... lcl proto */
1272     poppath(info);
1273   }
1274   poppath(info);
1275 
1276   /* Write upvalue names. */
1277   pushpath(info, ".upvalnames");
1278   for (i = 0; i < p->sizeupvalues; ++i) {
1279     pushpath(info, "[%d]", i);
1280     pushtstring(info->L, p->upvalues[i].name);          /* ... lcl proto name */
1281     persist(info);                                      /* ... lcl proto name */
1282     lua_pop(info->L, 1);                                     /* ... lcl proto */
1283     poppath(info);
1284   }
1285   poppath(info);
1286 }
1287 
1288 static void
u_proto(Info * info)1289 u_proto(Info *info) {                                            /* ... proto */
1290   int i, n;
1291   Proto *p = (Proto*)lua_touserdata(info->L, -1);
1292   eris_assert(p);
1293 
1294   eris_checkstack(info->L, 2);
1295 
1296   /* Preregister proto for handling of cycles (probably impossible, but
1297    * maybe via the constants of the proto... not worth taking the risk). */
1298   registerobject(info);
1299 
1300   /* Read general information. */
1301   p->linedefined = READ_VALUE(int);
1302   p->lastlinedefined = READ_VALUE(int);
1303   p->numparams = READ_VALUE(uint8_t);
1304   p->is_vararg = READ_VALUE(uint8_t);
1305   p->maxstacksize = READ_VALUE(uint8_t);
1306 
1307   /* Read byte code. */
1308   p->sizecode = READ_VALUE(int);
1309   eris_reallocvector(info->L, p->code, 0, p->sizecode, Instruction);
1310   READ(p->code, p->sizecode, Instruction);
1311 
1312   /* Read constants. */
1313   p->sizek = READ_VALUE(int);
1314   eris_reallocvector(info->L, p->k, 0, p->sizek, TValue);
1315   /* Set all values to nil to avoid confusing the GC. */
1316   for (i = 0, n = p->sizek; i < n; ++i) {
1317     eris_setnilvalue(&p->k[i]);
1318   }
1319   pushpath(info, ".constants");
1320   for (i = 0, n = p->sizek; i < n; ++i) {
1321     pushpath(info, "[%d]", i);
1322     unpersist(info);                                         /* ... proto obj */
1323     eris_setobj(info->L, &p->k[i], info->L->top - 1);
1324     lua_pop(info->L, 1);                                         /* ... proto */
1325     poppath(info);
1326   }
1327   poppath(info);
1328 
1329   /* Read child protos. */
1330   p->sizep = READ_VALUE(int);
1331   eris_reallocvector(info->L, p->p, 0, p->sizep, Proto*);
1332   /* Null all entries to avoid confusing the GC. */
1333   memset(p->p, 0, p->sizep * sizeof(Proto*));
1334   pushpath(info, ".protos");
1335   for (i = 0, n = p->sizep; i < n; ++i) {
1336     Proto *cp;
1337     pushpath(info, "[%d]", i);
1338     p->p[i] = eris_newproto(info->L);
1339     lua_pushlightuserdata(info->L, (void*)p->p[i]);              /* ... proto nproto */
1340     unpersist(info);                        /* ... proto nproto nproto/oproto */
1341     cp = (Proto*)lua_touserdata(info->L, -1);
1342     if (cp != p->p[i]) {                           /* ... proto nproto oproto */
1343       /* Just overwrite it, GC will clean this up. */
1344       p->p[i] = cp;
1345     }
1346     lua_pop(info->L, 2);                                         /* ... proto */
1347     poppath(info);
1348   }
1349   poppath(info);
1350 
1351   /* Read upvalues. */
1352   p->sizeupvalues = READ_VALUE(int);
1353   eris_reallocvector(info->L, p->upvalues, 0, p->sizeupvalues, Upvaldesc);
1354   for (i = 0, n = p->sizeupvalues; i < n; ++i) {
1355     p->upvalues[i].name = NULL;
1356     p->upvalues[i].instack = READ_VALUE(uint8_t);
1357     p->upvalues[i].idx = READ_VALUE(uint8_t);
1358   }
1359 
1360   /* Read debug information if any is present. */
1361   if (!READ_VALUE(uint8_t)) {
1362     return;
1363   }
1364 
1365   /* Read function source code. */
1366   unpersist(info);                                           /* ... proto str */
1367   copytstring(info->L, &p->source);
1368   lua_pop(info->L, 1);                                           /* ... proto */
1369 
1370   /* Read line information. */
1371   p->sizelineinfo = READ_VALUE(int);
1372   eris_reallocvector(info->L, p->lineinfo, 0, p->sizelineinfo, int);
1373   READ(p->lineinfo, p->sizelineinfo, int);
1374 
1375   /* Read locals info. */
1376   p->sizelocvars = READ_VALUE(int);
1377   eris_reallocvector(info->L, p->locvars, 0, p->sizelocvars, LocVar);
1378   /* Null the variable names to avoid confusing the GC. */
1379   for (i = 0, n = p->sizelocvars; i < n; ++i) {
1380     p->locvars[i].varname = NULL;
1381   }
1382   pushpath(info, ".locvars");
1383   for (i = 0, n = p->sizelocvars; i < n; ++i) {
1384     pushpath(info, "[%d]", i);
1385     p->locvars[i].startpc = READ_VALUE(int);
1386     p->locvars[i].endpc = READ_VALUE(int);
1387     unpersist(info);                                         /* ... proto str */
1388     copytstring(info->L, &p->locvars[i].varname);
1389     lua_pop(info->L, 1);                                         /* ... proto */
1390     poppath(info);
1391   }
1392   poppath(info);
1393 
1394   /* Read upvalue names. */
1395   pushpath(info, ".upvalnames");
1396   for (i = 0, n = p->sizeupvalues; i < n; ++i) {
1397     pushpath(info, "[%d]", i);
1398     unpersist(info);                                         /* ... proto str */
1399     copytstring(info->L, &p->upvalues[i].name);
1400     lua_pop(info->L, 1);                                         /* ... proto */
1401     poppath(info);
1402   }
1403   poppath(info);
1404   lua_pushvalue(info->L, -1);                              /* ... proto proto */
1405 
1406   eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1407 }
1408 
1409 /** ======================================================================== */
1410 
1411 static void
p_upval(Info * info)1412 p_upval(Info *info) {                                              /* ... obj */
1413   persist(info);                                                   /* ... obj */
1414 }
1415 
1416 static void
u_upval(Info * info)1417 u_upval(Info *info) {                                                  /* ... */
1418   eris_checkstack(info->L, 2);
1419 
1420   /* Create the table we use to store the stack location to the upval (1+2),
1421    * the value of the upval (3) and any references to the upvalue's value (4+).
1422    * References are stored as two entries each, the actual closure holding the
1423    * upvalue, and the index of the upvalue in that closure. */
1424   lua_createtable(info->L, 5, 0);                                  /* ... tbl */
1425   registerobject(info);
1426   unpersist(info);                                             /* ... tbl obj */
1427   lua_rawseti(info->L, -2, UVTVAL);                                /* ... tbl */
1428 
1429   eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1430 }
1431 
1432 /** ======================================================================== */
1433 
1434 /* For Lua closures we write the upvalue ID, which is usually the memory
1435  * address at which it is stored. This is used to tell which upvalues are
1436  * identical when unpersisting. */
1437 /* In either case we store the upvale *values*, i.e. the actual objects they
1438  * point to. As in Pluto, we will restore any upvalues of Lua closures as
1439  * closed as first, i.e. the upvalue will store the TValue itself. When
1440  * loading a thread containing the upvalue (meaning it's the actual owner of
1441  * the upvalue) we open it, i.e. we point it to the thread's upvalue list.
1442  * For C closures, upvalues are always closed. */
1443 static void
p_closure(Info * info)1444 p_closure(Info *info) {                              /* perms reftbl ... func */
1445   int nup;
1446   eris_checkstack(info->L, 2);
1447   switch (ttype(info->L->top - 1)) {
1448     case LUA_TLCF: /* light C function */
1449       /* We cannot persist these, they have to be handled via the permtable. */
1450       eris_error(info, ERIS_ERR_CFUNC, lua_tocfunction(info->L, -1));
1451       return; /* not reached */
1452     case LUA_TCCL: /* C closure */ {                  /* perms reftbl ... ccl */
1453       CClosure *cl = clCvalue(info->L->top - 1);
1454       /* Mark it as a C closure. */
1455       WRITE_VALUE(true, uint8_t);
1456       /* Write the upvalue count first, since we have to know it when creating
1457        * a new closure when unpersisting. */
1458       WRITE_VALUE(cl->nupvalues, uint8_t);
1459 
1460       /* We can only persist these if the underlying C function is in the
1461        * permtable. So we try to persist it first as a light C function. If it
1462        * isn't in the permtable that'll cause an error (in the case above). */
1463       lua_pushcfunction(info->L, lua_tocfunction(info->L, -1));
1464                                                 /* perms reftbl ... ccl cfunc */
1465       persist(info);                            /* perms reftbl ... ccl cfunc */
1466       lua_pop(info->L, 1);                            /* perms reftbl ... ccl */
1467 
1468       /* Persist the upvalues. Since for C closures all upvalues are always
1469        * closed we can just write the actual values. */
1470       pushpath(info, ".upvalues");
1471       for (nup = 1; nup <= cl->nupvalues; ++nup) {
1472         pushpath(info, "[%d]", nup);
1473         lua_getupvalue(info->L, -1, nup);         /* perms reftbl ... ccl obj */
1474         persist(info);                            /* perms reftbl ... ccl obj */
1475         lua_pop(info->L, 1);                          /* perms reftbl ... ccl */
1476         poppath(info);
1477       }
1478       poppath(info);
1479       break;
1480     }
1481     case LUA_TLCL: /* Lua function */ {               /* perms reftbl ... lcl */
1482       LClosure *cl = eris_clLvalue(info->L->top - 1);
1483       /* Mark it as a Lua closure. */
1484       WRITE_VALUE(false, uint8_t);
1485       /* Write the upvalue count first, since we have to know it when creating
1486        * a new closure when unpersisting. */
1487       WRITE_VALUE(cl->nupvalues, uint8_t);
1488 
1489       /* Persist the function's prototype. Pass the proto as a parameter to
1490        * p_proto so that it can access it and register it in the ref table. */
1491       pushpath(info, ".proto");
1492       lua_pushlightuserdata(info->L, cl->p);    /* perms reftbl ... lcl proto */
1493       lua_pushvalue(info->L, -1);         /* perms reftbl ... lcl proto proto */
1494       persist_keyed(info, LUA_TPROTO);          /* perms reftbl ... lcl proto */
1495       lua_pop(info->L, 1);                            /* perms reftbl ... lcl */
1496       poppath(info);
1497 
1498       /* Persist the upvalues. We pretend to write these as their own type,
1499        * to get proper identity preservation. We also pass them as a parameter
1500        * to p_upval so it can register the upvalue in the reference table. */
1501       pushpath(info, ".upvalues");
1502       for (nup = 1; nup <= cl->nupvalues; ++nup) {
1503         const char *name = lua_getupvalue(info->L, -1, nup);
1504                                                   /* perms reftbl ... lcl obj */
1505         pushpath(info, ".%s", name);
1506         lua_pushlightuserdata(info->L, lua_upvalueid(info->L, -2, nup));
1507                                                /* perms reftbl ... lcl obj id */
1508         persist_keyed(info, LUA_TUPVAL);          /* perms reftbl ... lcl obj */
1509         lua_pop(info->L, 1);                         /* perms reftble ... lcl */
1510         poppath(info);
1511       }
1512       poppath(info);
1513       break;
1514     }
1515     default:
1516       eris_error(info, ERIS_ERR_NOFUNC);
1517       return; /* not reached */
1518   }
1519 }
1520 
1521 static void
u_closure(Info * info)1522 u_closure(Info *info) {                                                /* ... */
1523   int nup;
1524   bool isCClosure = READ_VALUE(uint8_t);
1525   lu_byte nups = READ_VALUE(uint8_t);
1526   if (isCClosure) {
1527     lua_CFunction f;
1528 
1529     /* Reserve reference for the closure to avoid light C function or its
1530      * perm table key going first. */
1531     const int reference = ++(info->refcount);
1532 
1533     /* nups is guaranteed to be >= 1, otherwise it'd be a light C function. */
1534     eris_checkstack(info->L, nups < 2 ? 2 : nups);
1535 
1536     /* Read the C function from the permanents table. */
1537     unpersist(info);                                             /* ... cfunc */
1538     if (!lua_iscfunction(info->L, -1)) {
1539       eris_error(info, ERIS_ERR_UCFUNC, kTypenames[lua_type(info->L, -1)]);
1540     }
1541     f = lua_tocfunction(info->L, -1);
1542     if (!f) {
1543       eris_error(info, ERIS_ERR_UCFUNCNULL);
1544     }
1545     lua_pop(info->L, 1);                                               /* ... */
1546 
1547     /* Now this is a little roundabout, but we want to create the closure
1548      * before unpersisting the actual upvalues to avoid cycles. So we have to
1549      * create it with all nil first, then fill the upvalues in afterwards. */
1550     for (nup = 1; nup <= nups; ++nup) {
1551       lua_pushnil(info->L);                        /* ... nil[1] ... nil[nup] */
1552     }
1553     lua_pushcclosure(info->L, f, nups);                            /* ... ccl */
1554 
1555     /* Create the entry in the reftable. */
1556     lua_pushvalue(info->L, -1);                   /* perms reftbl ... ccl ccl */
1557     lua_rawseti(info->L, REFTIDX, reference);         /* perms reftbl ... ccl */
1558 
1559     /* Unpersist actual upvalues. */
1560     pushpath(info, ".upvalues");
1561     for (nup = 1; nup <= nups; ++nup) {
1562       pushpath(info, "[%d]", nup);
1563       unpersist(info);                                         /* ... ccl obj */
1564       lua_setupvalue(info->L, -2, nup);                            /* ... ccl */
1565       poppath(info);
1566     }
1567     poppath(info);
1568   }
1569   else {
1570     LClosure *cl;
1571     Proto *p;
1572 
1573     eris_checkstack(info->L, 4);
1574 
1575     /* Create closure and anchor it on the stack (avoid collection via GC). */
1576     cl = eris_newLclosure(info->L, nups);
1577     eris_setclLvalue(info->L, info->L->top, cl);                   /* ... lcl */
1578     api_incr_top(info->L);
1579 
1580     /* Preregister closure for handling of cycles (upvalues). */
1581     registerobject(info);
1582 
1583     /* Read prototype. In general, we create protos (and upvalues) before
1584      * trying to read them and pass a pointer to the instance along to the
1585      * unpersist function. This way the instance is safely hooked up to an
1586      * object, so we don't have to worry about it getting GCed. */
1587     pushpath(info, ".proto");
1588     cl->p = eris_newproto(info->L);
1589     /* Push the proto into which to unpersist as a parameter to u_proto. */
1590     lua_pushlightuserdata(info->L, cl->p);                /* ... lcl nproto */
1591     unpersist(info);                          /* ... lcl nproto nproto/oproto */
1592     eris_assert(lua_type(info->L, -1) == LUA_TLIGHTUSERDATA);
1593     /* The proto we have now may differ, if we already unpersisted it before.
1594      * In that case we now have a reference to the originally unpersisted
1595      * proto so we'll use that. */
1596     p = (Proto*)lua_touserdata(info->L, -1);
1597     if (p != cl->p) {                              /* ... lcl nproto oproto */
1598       /* Just overwrite the old one, GC will clean this up. */
1599       cl->p = p;
1600     }
1601     lua_pop(info->L, 2);                                           /* ... lcl */
1602     eris_assert(cl->nupvalues == cl->p->sizeupvalues);
1603     eris_initupvals(info->L, cl); /* Init to all closed, fill in later. */
1604     poppath(info);
1605 
1606     /* Unpersist all upvalues. */
1607     pushpath(info, ".upvalues");
1608     for (nup = 1; nup <= nups; ++nup) {
1609       UpVal **uv = &cl->upvals[nup - 1];
1610       /* Get the actual name of the upvalue, if possible. */
1611       if (p->upvalues[nup - 1].name) {
1612         pushpath(info, "[%s]", getstr(p->upvalues[nup - 1].name));
1613       }
1614       else {
1615         pushpath(info, "[%d]", nup);
1616       }
1617       unpersist(info);                                         /* ... lcl tbl */
1618       eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
1619       lua_rawgeti(info->L, -1, UVTOCL);               /* ... lcl tbl olcl/nil */
1620       if (lua_isnil(info->L, -1)) {                        /* ... lcl tbl nil */
1621         lua_pop(info->L, 1);                                   /* ... lcl tbl */
1622         lua_pushvalue(info->L, -2);                        /* ... lcl tbl lcl */
1623         lua_rawseti(info->L, -2, UVTOCL);                      /* ... lcl tbl */
1624         lua_pushinteger(info->L, nup);                     /* ... lcl tbl nup */
1625         lua_rawseti(info->L, -2, UVTONU);                      /* ... lcl tbl */
1626       }
1627       else {                                              /* ... lcl tbl olcl */
1628         int onup;
1629         eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
1630         lua_rawgeti(info->L, -2, UVTONU);            /* ... lcl tbl olcl onup */
1631         eris_assert(lua_type(info->L, -1) == LUA_TNUMBER);
1632         onup = lua_tointeger(info->L, -1);
1633         lua_pop(info->L, 1);                              /* ... lcl tbl olcl */
1634         lua_upvaluejoin(info->L, -3, nup, -1, onup);
1635         lua_pop(info->L, 1);                                   /* ... lcl tbl */
1636       }
1637 
1638       /* Set the upvalue's actual value and add our reference to the upvalue to
1639        * the list, for reference patching if we have to open the upvalue in
1640        * u_thread. Either is only necessary if the upvalue is still closed. */
1641       if (!upisopen(*uv)) {
1642         int i;
1643         /* Always update the value of the upvalue's value for closed upvalues,
1644          * even if we re-used one - if we had a cycle, it might have been
1645          * incorrectly initialized to nil before (or rather, not yet set). */
1646         lua_rawgeti(info->L, -1, UVTVAL);                  /* ... lcl tbl obj */
1647         eris_setobj(info->L, &(*uv)->u.value, info->L->top - 1);
1648         lua_pop(info->L, 1);                                   /* ... lcl tbl */
1649 
1650         lua_pushinteger(info->L, nup);                     /* ... lcl tbl nup */
1651         lua_pushvalue(info->L, -3);                    /* ... lcl tbl nup lcl */
1652         if (luaL_len(info->L, -3) >= UVTVAL) {
1653           /* Got a valid sequence (value already set), insert at the end. */
1654           i = luaL_len(info->L, -3);
1655           lua_rawseti(info->L, -3, i + 1);                 /* ... lcl tbl nup */
1656           lua_rawseti(info->L, -2, i + 2);                     /* ... lcl tbl */
1657         }
1658         else {                                         /* ... lcl tbl nup lcl */
1659           /* Find where to insert. This can happen if we have cycles, in which
1660            * case the table is not fully initialized at this point, i.e. the
1661            * value is not in it, yet (we work around that by always setting it,
1662            * as seen above). */
1663           for (i = UVTREF;; i += 2) {
1664             lua_rawgeti(info->L, -3, i);       /* ... lcl tbl nup lcl lcl/nil */
1665             if (lua_isnil(info->L, -1)) {          /* ... lcl tbl nup lcl nil */
1666               lua_pop(info->L, 1);                     /* ... lcl tbl nup lcl */
1667               lua_rawseti(info->L, -3, i);                 /* ... lcl tbl nup */
1668               lua_rawseti(info->L, -2, i + 1);                 /* ... lcl tbl */
1669               break;
1670             }
1671             else {
1672               lua_pop(info->L, 1);                     /* ... lcl tbl nup lcl */
1673             }
1674           }                                                    /* ... lcl tbl */
1675         }
1676       }
1677 
1678       lua_pop(info->L, 1);                                         /* ... lcl */
1679       poppath(info);
1680     }
1681     poppath(info);
1682 
1683     /* save it in cache for reuse, see lvm.c:416 */
1684     if (!isblack(p))
1685       p->cache = cl;
1686   }
1687 
1688   eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
1689 }
1690 
1691 /** ======================================================================== */
1692 
1693 static void
p_thread(Info * info)1694 p_thread(Info *info) {                                          /* ... thread */
1695   lua_State* thread = lua_tothread(info->L, -1);
1696   size_t level = 0, total = thread->top - thread->stack;
1697   CallInfo *ci;
1698   UpVal *uv;
1699 
1700   eris_checkstack(info->L, 2);
1701 
1702   /* We cannot persist any running threads, because by definition we *are* that
1703    * running thread. And we use the stack. So yeah, really not a good idea. */
1704   if (thread == info->L) {
1705     eris_error(info, ERIS_ERR_THREAD);
1706     return; /* not reached */
1707   }
1708 
1709   /* Persist the stack. Save the total size and used space first. */
1710   WRITE_VALUE(thread->stacksize, int);
1711   WRITE_VALUE(total, size_t);
1712 
1713   /* The Lua stack looks like this:
1714    * stack ... top ... stack_last
1715    * Where stack <= top <= stack_last, and "top" actually being the first free
1716    * element, i.e. there's nothing stored there. So we stop one below that. */
1717   pushpath(info, ".stack");
1718   lua_pushnil(info->L);                                     /* ... thread nil */
1719   /* Since the thread's stack may be re-allocated in the meantime, we cannot
1720    * use pointer arithmetic here (i.e. o = thread->stack; ...; ++o). Instead we
1721    * have to keep track of our position in the stack directly (which we do for
1722    * the path info anyway) and compute the actual address each time.
1723    */
1724   for (; level < total; ++level) {
1725     pushpath(info, "[%d]", level);
1726     eris_setobj(info->L, info->L->top - 1, thread->stack + level);
1727                                                             /* ... thread obj */
1728     persist(info);                                          /* ... thread obj */
1729     poppath(info);
1730   }
1731   lua_pop(info->L, 1);                                          /* ... thread */
1732   poppath(info);
1733 
1734   /* If the thread isn't running this must be the default value, which is 1. */
1735   eris_assert(thread->nny == 1);
1736 
1737   /* Error jump info should only be set while thread is running. */
1738   eris_assert(thread->errorJmp == NULL);
1739 
1740   /* thread->oldpc always seems to be uninitialized, at least gdb always shows
1741    * it as 0xbaadf00d when I set a breakpoint here. */
1742 
1743   /* Write general information. */
1744   WRITE_VALUE(thread->status, uint8_t);
1745   WRITE_VALUE(eris_savestackidx(thread,
1746     eris_restorestack(thread, thread->errfunc)), size_t);
1747   /* These are only used while a thread is being executed or can be deduced:
1748   WRITE_VALUE(thread->nCcalls, uint16_t);
1749   WRITE_VALUE(thread->allowhook, uint8_t); */
1750 
1751   /* Hooks are not supported, bloody can of worms, those.
1752   WRITE_VALUE(thread->hookmask, uint8_t);
1753   WRITE_VALUE(thread->basehookcount, int);
1754   WRITE_VALUE(thread->hookcount, int); */
1755 
1756   if (thread->hook) {
1757     /* TODO Warn that hooks are not persisted? */
1758   }
1759 
1760   /* Write call information (stack frames). In 5.2 CallInfo is stored in a
1761    * linked list that originates in thead.base_ci. Upon initialization the
1762    * thread.ci is set to thread.base_ci. During thread calls this is extended
1763    * and always represents the tail of the callstack, though not necessarily of
1764    * the linked list (which can be longer if the callstack was deeper earlier,
1765    * but shrunk due to returns). */
1766   pushpath(info, ".callinfo");
1767   level = 0;
1768   eris_assert(&thread->base_ci != thread->ci->next);
1769   for (ci = &thread->base_ci; ci != thread->ci->next; ci = ci->next) {
1770     pushpath(info, "[%d]", level++);
1771     WRITE_VALUE(eris_savestackidx(thread, ci->func), size_t);
1772     WRITE_VALUE(eris_savestackidx(thread, ci->top), size_t);
1773     WRITE_VALUE(ci->nresults, int16_t);
1774     WRITE_VALUE(ci->callstatus, uint8_t);
1775     if (ci->callstatus) {
1776       /* CallInfo.extra is used in two contexts: if L->status == LUA_YIELD and
1777        * CallInfo is the one stored as L->ci, in which case ci->extra refers to
1778        * the original value of L->ci->func, and when we have a yieldable pcall
1779        * (ci->callstatus & CIST_YPCALL) where ci->extra holds the stack level
1780        * of the function being called (see lua_pcallk). We save the ci->extra
1781        * for L->ci after the loop, because we won't know which one it is when
1782        * unpersisting. */
1783       if (ci->callstatus & CIST_YPCALL) {
1784         WRITE_VALUE(eris_savestackidx(thread,
1785           eris_restorestack(thread, ci->extra)), size_t);
1786       }
1787 
1788       eris_assert(eris_isLua(ci) || (ci->callstatus & CIST_TAIL) == 0);
1789       if (ci->callstatus & CIST_HOOKYIELD) {
1790         eris_error(info, ERIS_ERR_HOOK);
1791       }
1792 
1793       if (eris_isLua(ci)) {
1794         const LClosure *lcl = eris_ci_func(ci);
1795         WRITE_VALUE(eris_savestackidx(thread, ci->u.l.base), size_t);
1796         WRITE_VALUE(ci->u.l.savedpc - lcl->p->code, size_t);
1797       }
1798       else {
1799         /* These are only used while a thread is being executed:
1800         WRITE_VALUE(ci->u.c.old_errfunc, ptrdiff_t); */
1801         if (thread->status == LUA_YIELD && ci->u.c.k) {
1802           WRITE_VALUE(true, uint8_t);
1803           WRITE_VALUE(ci->u.c.ctx, int);
1804           /* NOTE Ugly hack. We have to push the continuation function as a C
1805            * function to properly track it in our ref table. It's never called,
1806            * so we can get away with this. */
1807           lua_pushcfunction(info->L, (lua_CFunction)ci->u.c.k);
1808                                                              /* ... thread func */
1809           persist(info);                                 /* ... thread func/nil */
1810           lua_pop(info->L, 1);                                    /* ... thread */
1811         }
1812         else {
1813           WRITE_VALUE(false, uint8_t);
1814         }
1815       }
1816     }
1817 
1818     /* Write whether there's more to come. */
1819     WRITE_VALUE(ci->next == thread->ci->next, uint8_t);
1820 
1821     poppath(info);
1822   }
1823   /** See comment on ci->extra in loop. */
1824   if (thread->status == LUA_YIELD) {
1825     WRITE_VALUE(eris_savestackidx(thread,
1826         eris_restorestack(thread, thread->ci->extra)), size_t);
1827   }
1828   poppath(info);
1829 
1830   pushpath(info, ".openupval");
1831   lua_pushnil(info->L);                                     /* ... thread nil */
1832   level = 0;
1833   for (uv = thread->openupval;
1834        uv != NULL;
1835        uv = uv->u.open.next)
1836   {
1837     pushpath(info, "[%d]", level++);
1838     WRITE_VALUE(eris_savestackidx(thread, uv->v) + 1, size_t);
1839     eris_setobj(info->L, info->L->top - 1, uv->v);          /* ... thread obj */
1840     lua_pushlightuserdata(info->L, uv);                  /* ... thread obj id */
1841     persist_keyed(info, LUA_TUPVAL);                        /* ... thread obj */
1842     poppath(info);
1843   }
1844   WRITE_VALUE(0, size_t);
1845   lua_pop(info->L, 1);                                          /* ... thread */
1846   poppath(info);
1847 }
1848 
1849 /* Used in u_thread to validate read stack positions. */
1850 #define validate(stackpos, inclmax) \
1851   if ((stackpos) < thread->stack || stackpos > (inclmax)) { \
1852     (stackpos) = thread->stack; \
1853     eris_error(info, ERIS_ERR_STACKBOUNDS); }
1854 
1855 /* I had so hoped to get by without any 'hacks', but I surrender. We mark the
1856  * thread as incomplete to avoid the GC messing with it while we're building
1857  * it. Otherwise it may try to shrink its stack. We do this by setting its
1858  * stack field to null for every call that may trigger a GC run, since that
1859  * field is what's used to determine whether threads should be shrunk. See
1860  * lgc.c:699. Some of the locks could probably be joined (since nothing
1861  * inbetween requires the stack field to be valid), but I prefer to keep the
1862  * "invalid" blocks as small as possible to make it clearer. Also, locking and
1863  * unlocking are really just variable assignments, so they're really cheap. */
1864 #define LOCK(L) (L->stack = NULL)
1865 #define UNLOCK(L) (L->stack = stack)
1866 
1867 static void
u_thread(Info * info)1868 u_thread(Info *info) {                                                 /* ... */
1869   lua_State* thread;
1870   size_t level;
1871   StkId stack, o;
1872 
1873   eris_checkstack(info->L, 3);
1874 
1875   thread = lua_newthread(info->L);                              /* ... thread */
1876   registerobject(info);
1877 
1878   /* Unpersist the stack. Read size first and adjust accordingly. */
1879   eris_reallocstack(thread, READ_VALUE(int));
1880   stack = thread->stack; /* After the realloc in case the address changes. */
1881   thread->top = thread->stack + READ_VALUE(size_t);
1882   validate(thread->top, thread->stack_last);
1883 
1884   /* Read the elements one by one. */
1885   LOCK(thread);
1886   pushpath(info, ".stack");
1887   UNLOCK(thread);
1888   level = 0;
1889   for (o = stack; o < thread->top; ++o) {
1890     LOCK(thread);
1891     pushpath(info, "[%d]", level++);
1892     unpersist(info);                                        /* ... thread obj */
1893     UNLOCK(thread);
1894     eris_setobj(thread, o, info->L->top - 1);
1895     lua_pop(info->L, 1);                                        /* ... thread */
1896     LOCK(thread);
1897     poppath(info);
1898     UNLOCK(thread);
1899   }
1900   LOCK(thread);
1901   poppath(info);
1902   UNLOCK(thread);
1903 
1904   /* As in p_thread, just to make sure. */
1905   eris_assert(thread->nny == 1);
1906   eris_assert(thread->errorJmp == NULL);
1907   eris_assert(thread->hook == NULL);
1908 
1909   /* See comment in persist. */
1910   thread->oldpc = NULL;
1911 
1912   /* Read general information. */
1913   thread->status = READ_VALUE(uint8_t);
1914   thread->errfunc = eris_savestack(thread,
1915     eris_restorestackidx(thread, READ_VALUE(size_t)));
1916   if (thread->errfunc) {
1917     o = eris_restorestack(thread, thread->errfunc);
1918     validate(o, thread->top);
1919     if (eris_ttnov(o) != LUA_TFUNCTION) {
1920       eris_error(info, ERIS_ERR_THREADERRF);
1921     }
1922   }
1923   /* These are only used while a thread is being executed or can be deduced:
1924   thread->nCcalls = READ_VALUE(uint16_t);
1925   thread->allowhook = READ_VALUE(uint8_t); */
1926   eris_assert(thread->allowhook == 1);
1927 
1928   /* Not supported.
1929   thread->hookmask = READ_VALUE(uint8_t);
1930   thread->basehookcount = READ_VALUE(int);
1931   thread->hookcount = READ_VALUE(int); */
1932 
1933   /* Read call information (stack frames). */
1934   LOCK(thread);
1935   pushpath(info, ".callinfo");
1936   UNLOCK(thread);
1937   thread->ci = &thread->base_ci;
1938   level = 0;
1939   for (;;) {
1940     LOCK(thread);
1941     pushpath(info, "[%d]", level++);
1942     UNLOCK(thread);
1943     thread->ci->func = eris_restorestackidx(thread, READ_VALUE(size_t));
1944     validate(thread->ci->func, thread->top - 1);
1945     thread->ci->top = eris_restorestackidx(thread, READ_VALUE(size_t));
1946     validate(thread->ci->top, thread->stack_last);
1947     thread->ci->nresults = READ_VALUE(int16_t);
1948     thread->ci->callstatus = READ_VALUE(uint8_t);
1949     if (thread->ci->callstatus) {
1950       /** See comment in p_thread. */
1951       if (thread->ci->callstatus & CIST_YPCALL) {
1952         thread->ci->extra = eris_savestack(thread,
1953           eris_restorestackidx(thread, READ_VALUE(size_t)));
1954         o = eris_restorestack(thread, thread->ci->extra);
1955         validate(o, thread->top);
1956         if (eris_ttnov(o) != LUA_TFUNCTION) {
1957           eris_error(info, ERIS_ERR_THREADCI);
1958         }
1959       }
1960 
1961       if (eris_isLua(thread->ci)) {
1962         LClosure *lcl = eris_ci_func(thread->ci);
1963         thread->ci->u.l.base = eris_restorestackidx(thread, READ_VALUE(size_t));
1964         validate(thread->ci->u.l.base, thread->top);
1965         thread->ci->u.l.savedpc = lcl->p->code + READ_VALUE(size_t);
1966         if (thread->ci->u.l.savedpc < lcl->p->code ||
1967             thread->ci->u.l.savedpc > lcl->p->code + lcl->p->sizecode)
1968         {
1969           thread->ci->u.l.savedpc = lcl->p->code; /* Just to be safe. */
1970           eris_error(info, ERIS_ERR_THREADPC);
1971         }
1972       }
1973       else {
1974         /* These are only used while a thread is being executed:
1975         thread->ci->u.c.old_errfunc = READ_VALUE(ptrdiff_t); */
1976         thread->ci->u.c.old_errfunc = 0;
1977 
1978         if (thread->status == LUA_YIELD && READ_VALUE(uint8_t)) {
1979           thread->ci->u.c.ctx = READ_VALUE(int);
1980           LOCK(thread);
1981           unpersist(info);                                  /* ... thread func? */
1982           UNLOCK(thread);
1983           if (lua_iscfunction(info->L, -1)) {                /* ... thread func */
1984             /* NOTE Ugly hack. See p_thread. */
1985             thread->ci->u.c.k = (lua_KFunction)lua_tocfunction(info->L, -1);
1986           }
1987           else {
1988             eris_error(info, ERIS_ERR_THREADCTX);
1989             return; /* not reached */
1990           }
1991           lua_pop(info->L, 1);                                    /* ... thread */
1992         }
1993         else {
1994           thread->ci->u.c.ctx = 0;
1995           thread->ci->u.c.k = NULL;
1996         }
1997       }
1998       LOCK(thread);
1999       poppath(info);
2000       UNLOCK(thread);
2001     }
2002     else {
2003       thread->ci->u.c.k = NULL;
2004       thread->ci->u.c.old_errfunc = 0;
2005       thread->ci->u.c.ctx = 0;
2006     }
2007 
2008     /* Read in value for check for next iteration. */
2009     if (READ_VALUE(uint8_t)) {
2010       break;
2011     }
2012     else {
2013       thread->ci = eris_extendCI(thread);
2014     }
2015   }
2016   if (thread->status == LUA_YIELD) {
2017     thread->ci->extra = eris_savestack(thread,
2018       eris_restorestackidx(thread, READ_VALUE(size_t)));
2019     o = eris_restorestack(thread, thread->ci->extra);
2020     validate(o, thread->top);
2021     if (eris_ttnov(o) != LUA_TFUNCTION) {
2022       eris_error(info, ERIS_ERR_THREADCI);
2023     }
2024   }
2025   LOCK(thread);
2026   poppath(info);
2027   UNLOCK(thread);
2028 
2029   /* Get from context: only zero for dead threads, otherwise one. */
2030   thread->nCcalls = thread->status != LUA_OK || lua_gettop(thread) != 0;
2031 
2032   /* Proceed to open upvalues. These upvalues will already exist due to the
2033    * functions using them having been unpersisted (they'll usually be in the
2034    * stack of the thread). For this reason we store all previous references to
2035    * the upvalue in a table that is returned when we try to unpersist an
2036    * upvalue, so that we can adjust these references in here. */
2037   LOCK(thread);
2038   pushpath(info, ".openupval");
2039   UNLOCK(thread);
2040   level = 0;
2041   for (;;) {
2042     UpVal *nuv;
2043     StkId stk;
2044     /* Get the position of the upvalue on the stack. As a special value we pass
2045      * zero to indicate there are no more upvalues. */
2046     const size_t offset = READ_VALUE(size_t);
2047     if (offset == 0) {
2048       break;
2049     }
2050     LOCK(thread);
2051     pushpath(info, "[%d]", level);
2052     UNLOCK(thread);
2053     stk = eris_restorestackidx(thread, offset - 1);
2054     validate(stk, thread->top - 1);
2055     LOCK(thread);
2056     unpersist(info);                                        /* ... thread tbl */
2057     UNLOCK(thread);
2058     eris_assert(lua_type(info->L, -1) == LUA_TTABLE);
2059 
2060     /* Create the open upvalue either way. */
2061     LOCK(thread);
2062     nuv = eris_findupval(thread, stk);
2063     UNLOCK(thread);
2064 
2065     /* Then check if we need to patch some references. */
2066     lua_rawgeti(info->L, -1, UVTREF);               /* ... thread tbl lcl/nil */
2067     if (!lua_isnil(info->L, -1)) {                      /* ... thread tbl lcl */
2068       int i, n;
2069       eris_assert(lua_type(info->L, -1) == LUA_TFUNCTION);
2070       /* Already exists, replace it. To do this we have to patch all the
2071        * references to the already existing one, which we added to the table in
2072        * u_closure. */
2073       lua_pop(info->L, 1);                                  /* ... thread tbl */
2074       for (i = UVTREF, n = luaL_len(info->L, -1); i <= n; i += 2) {
2075         LClosure *cl;
2076         int nup;
2077         lua_rawgeti(info->L, -1, i);                    /* ... thread tbl lcl */
2078         cl = eris_clLvalue(info->L->top - 1);
2079         lua_pop(info->L, 1);                                /* ... thread tbl */
2080         lua_rawgeti(info->L, -1, i + 1);                /* ... thread tbl nup */
2081         nup = lua_tointeger(info->L, -1);
2082         lua_pop(info->L, 1);                                /* ... thread tbl */
2083         /* Open the upvalue by pointing to the stack and register in GC. */
2084         cl->upvals[nup - 1] = nuv;
2085         cl->upvals[nup - 1]->refcount++;
2086       }
2087     }
2088     else {                                              /* ... thread tbl nil */
2089       eris_assert(lua_isnil(info->L, -1));
2090       lua_pop(info->L, 1);                                  /* ... thread tbl */
2091     }
2092 
2093     /* Store open upvalue in table for future references. */
2094     LOCK(thread);
2095     lua_pop(info->L, 1);                                        /* ... thread */
2096     poppath(info);
2097     UNLOCK(thread);
2098   }
2099   poppath(info);
2100 
2101   eris_assert(lua_type(info->L, -1) == LUA_TTHREAD);
2102 }
2103 
2104 #undef UNLOCK
2105 #undef LOCK
2106 
2107 #undef validate
2108 
2109 /*
2110 ** ============================================================================
2111 ** Top-level delegator.
2112 ** ============================================================================
2113 */
2114 
2115 static void
persist_typed(Info * info,int type)2116 persist_typed(Info *info, int type) {                 /* perms reftbl ... obj */
2117   eris_ifassert(const int top = lua_gettop(info->L));
2118   if (info->level >= info->maxComplexity) {
2119     eris_error(info, ERIS_ERR_COMPLEXITY);
2120   }
2121   ++info->level;
2122 
2123   WRITE_VALUE(type, int);
2124   switch(type) {
2125     case LUA_TBOOLEAN:
2126       p_boolean(info);
2127       break;
2128     case LUA_TLIGHTUSERDATA:
2129       p_pointer(info);
2130       break;
2131     case LUA_TNUMBER:
2132       p_number(info);
2133       break;
2134     case LUA_TSTRING:
2135       p_string(info);
2136       break;
2137     case LUA_TTABLE:
2138       p_table(info);
2139       break;
2140     case LUA_TFUNCTION:
2141       p_closure(info);
2142       break;
2143     case LUA_TUSERDATA:
2144       p_userdata(info);
2145       break;
2146     case LUA_TTHREAD:
2147       p_thread(info);
2148       break;
2149     case LUA_TPROTO:
2150       p_proto(info);
2151       break;
2152     case LUA_TUPVAL:
2153       p_upval(info);
2154       break;
2155     default:
2156       eris_error(info, ERIS_ERR_TYPEP, type);
2157   }                                                   /* perms reftbl ... obj */
2158 
2159   --info->level;
2160   eris_assert(top == lua_gettop(info->L));
2161 }
2162 
2163 /* Second-level delegating persist function, used for cases when persisting
2164  * data that's stored in the reftable with a key that is not the data itself,
2165  * namely upvalues and protos. */
2166 static void
persist_keyed(Info * info,int type)2167 persist_keyed(Info *info, int type) {          /* perms reftbl ... obj refkey */
2168   eris_checkstack(info->L, 2);
2169 
2170   /* Keep a copy of the key for pushing it to the reftable, if necessary. */
2171   lua_pushvalue(info->L, -1);           /* perms reftbl ... obj refkey refkey */
2172 
2173   /* If the object has already been written, write a reference to it. */
2174   lua_rawget(info->L, REFTIDX);           /* perms reftbl ... obj refkey ref? */
2175   if (!lua_isnil(info->L, -1)) {           /* perms reftbl ... obj refkey ref */
2176     const int reference = lua_tointeger(info->L, -1);
2177     WRITE_VALUE(reference + ERIS_REFERENCE_OFFSET, int);
2178     lua_pop(info->L, 2);                              /* perms reftbl ... obj */
2179     return;
2180   }                                        /* perms reftbl ... obj refkey nil */
2181   lua_pop(info->L, 1);                         /* perms reftbl ... obj refkey */
2182 
2183   /* Copy the refkey for the perms check below. */
2184   lua_pushvalue(info->L, -1);           /* perms reftbl ... obj refkey refkey */
2185 
2186   /* Put the value in the reference table. This creates an entry pointing from
2187    * the object (or its key) to the id the object is referenced by. */
2188   lua_pushinteger(info->L, ++(info->refcount));
2189                                     /* perms reftbl ... obj refkey refkey ref */
2190   lua_rawset(info->L, REFTIDX);                /* perms reftbl ... obj refkey */
2191 
2192   /* At this point, we'll give the permanents table a chance to play. */
2193   lua_gettable(info->L, PERMIDX);            /* perms reftbl ... obj permkey? */
2194   if (!lua_isnil(info->L, -1)) {              /* perms reftbl ... obj permkey */
2195     type = lua_type(info->L, -2);
2196     /* Prepend permanent "type" so that we know it's a permtable key. This will
2197      * trigger u_permanent when unpersisting. Also write the original type, so
2198      * that we can verify what we get in the permtable when unpersisting is of
2199      * the same kind we had when persisting. */
2200     WRITE_VALUE(ERIS_PERMANENT, int);
2201     WRITE_VALUE(type, uint8_t);
2202     persist(info);                            /* perms reftbl ... obj permkey */
2203     lua_pop(info->L, 1);                              /* perms reftbl ... obj */
2204   }
2205   else {                                          /* perms reftbl ... obj nil */
2206     /* No entry in the permtable for this object, persist it directly. */
2207     lua_pop(info->L, 1);                              /* perms reftbl ... obj */
2208     persist_typed(info, type);                        /* perms reftbl ... obj */
2209   }                                                   /* perms reftbl ... obj */
2210 }
2211 
2212 /* Top-level delegating persist function. */
2213 static void
persist(Info * info)2214 persist(Info *info) {                                 /* perms reftbl ... obj */
2215   /* Grab the object's type. */
2216   const int type = lua_type(info->L, -1);
2217 
2218   /* If the object is nil, only write its type. */
2219   if (type == LUA_TNIL) {
2220     WRITE_VALUE(type, int);
2221   }
2222   /* Write simple values directly, because writing a "reference" would take up
2223    * just as much space and we can save ourselves work this way. */
2224   else if (type == LUA_TBOOLEAN ||
2225            type == LUA_TLIGHTUSERDATA ||
2226            type == LUA_TNUMBER)
2227   {
2228     persist_typed(info, type);                        /* perms reftbl ... obj */
2229   }
2230   /* For all non-simple values we keep a record in the reftable, so that we
2231    * keep references alive across persisting and unpersisting an object. This
2232    * has the nice side-effect of saving some space. */
2233   else {
2234     eris_checkstack(info->L, 1);
2235     lua_pushvalue(info->L, -1);                   /* perms reftbl ... obj obj */
2236     persist_keyed(info, type);                        /* perms reftbl ... obj */
2237   }
2238 }
2239 
2240 /** ======================================================================== */
2241 
2242 static void
u_permanent(Info * info)2243 u_permanent(Info *info) {                                 /* perms reftbl ... */
2244   const int type = READ_VALUE(uint8_t);
2245   /* Reserve reference to avoid the key going first. */
2246   const int reference = ++(info->refcount);
2247   eris_checkstack(info->L, 1);
2248   unpersist(info);                                /* perms reftbl ... permkey */
2249   lua_gettable(info->L, PERMIDX);                    /* perms reftbl ... obj? */
2250   if (lua_isnil(info->L, -1)) {                       /* perms reftbl ... nil */
2251     /* Since we may need permanent values to rebuild other structures, namely
2252      * closures and threads, we cannot allow perms to fail unpersisting. */
2253     eris_error(info, ERIS_ERR_SPER_UPERMNIL);
2254   }
2255   else if (lua_type(info->L, -1) != type) {            /* perms reftbl ... :( */
2256     /* For the same reason that we cannot allow nil we must also require the
2257      * unpersisted value to be of the correct type. */
2258     const char *want = kTypenames[type];
2259     const char *have = kTypenames[lua_type(info->L, -1)];
2260     eris_error(info, ERIS_ERR_SPER_UPERM, want, have);
2261   }                                                   /* perms reftbl ... obj */
2262   /* Create the entry in the reftable. */
2263   lua_pushvalue(info->L, -1);                     /* perms reftbl ... obj obj */
2264   lua_rawseti(info->L, REFTIDX, reference);           /* perms reftbl ... obj */
2265 }
2266 
2267 static void
unpersist(Info * info)2268 unpersist(Info *info) {                                   /* perms reftbl ... */
2269   eris_ifassert(const int top = lua_gettop(info->L));
2270   if (info->level >= info->maxComplexity) {
2271     eris_error(info, ERIS_ERR_COMPLEXITY);
2272   }
2273   ++info->level;
2274 
2275   eris_checkstack(info->L, 1);
2276   {
2277     const int typeOrReference = READ_VALUE(int);
2278     if (typeOrReference > ERIS_REFERENCE_OFFSET) {
2279       const int reference = typeOrReference - ERIS_REFERENCE_OFFSET;
2280       lua_rawgeti(info->L, REFTIDX, reference);   /* perms reftbl ud ... obj? */
2281       if (lua_isnil(info->L, -1)) {                 /* perms reftbl ud ... :( */
2282         eris_error(info, ERIS_ERR_REF, reference);
2283       }                                            /* perms reftbl ud ... obj */
2284     }
2285     else {
2286       const int type = typeOrReference;
2287       switch (type) {
2288         case LUA_TNIL:
2289           lua_pushnil(info->L);
2290           break;
2291         case LUA_TBOOLEAN:
2292           u_boolean(info);
2293           break;
2294         case LUA_TLIGHTUSERDATA:
2295           u_pointer(info);
2296           break;
2297         case LUA_TNUMBER:
2298           u_number(info);
2299           break;
2300         case LUA_TSTRING:
2301           u_string(info);
2302           break;
2303         case LUA_TTABLE:
2304           u_table(info);
2305           break;
2306         case LUA_TFUNCTION:
2307           u_closure(info);
2308           break;
2309         case LUA_TUSERDATA:
2310           u_userdata(info);
2311           break;
2312         case LUA_TTHREAD:
2313           u_thread(info);
2314           break;
2315         case LUA_TPROTO:
2316           u_proto(info);
2317           break;
2318         case LUA_TUPVAL:
2319           u_upval(info);
2320           break;
2321         case ERIS_PERMANENT:
2322           u_permanent(info);
2323           break;
2324         default:
2325           eris_error(info, ERIS_ERR_TYPEU, type);
2326       }                                              /* perms reftbl ... obj? */
2327     }
2328   }
2329 
2330   --info->level;
2331   eris_assert(top + 1 == lua_gettop(info->L));
2332 }
2333 
2334 /* }======================================================================== */
2335 
2336 /*
2337 ** {===========================================================================
2338 ** Writer and reader implementation for library calls.
2339 ** ============================================================================
2340 */
2341 
2342 /* Implementation note: we use the MBuffer struct, but we don't use the built-
2343  * in reallocation functions since we'll keep our working copy on the stack, to
2344  * allow for proper collection if we have to throw an error. This is very much
2345  * what the auxlib does with its buffer functionality. Which we don't use since
2346  * we cannot guarantee stack balance inbetween calls to luaL_add*. */
2347 
2348 static int
writer(lua_State * L,const void * p,size_t sz,void * ud)2349 writer(lua_State *L, const void *p, size_t sz, void *ud) {
2350                                                /* perms reftbl buff path? ... */
2351   const char *value = (const char*)p;
2352   Mbuffer *buff = (Mbuffer*)ud;
2353   const size_t size = eris_bufflen(buff);
2354   const size_t capacity = eris_sizebuffer(buff);
2355   if (capacity - size < sz) {
2356     size_t newcapacity = capacity * 2; /* overflow checked below */
2357     if (newcapacity - size < sz) {
2358       newcapacity = capacity + sz; /* overflow checked below */
2359     }
2360     if (newcapacity <= capacity) {
2361       /* Overflow in capacity, buffer size limit reached. */
2362       return 1;
2363     } else {
2364       char *newbuff;
2365       eris_checkstack(L, 1);
2366       newbuff = (char*)lua_newuserdata(L, newcapacity * sizeof(char));
2367                                          /* perms reftbl buff path? ... nbuff */
2368       memcpy(newbuff, eris_buffer(buff), eris_bufflen(buff)); // NOLINT
2369       lua_replace(L, BUFFIDX);                /* perms reftbl nbuff path? ... */
2370       eris_buffer(buff) = newbuff;
2371       eris_sizebuffer(buff) = newcapacity;
2372     }
2373   }
2374   memcpy(&eris_buffer(buff)[eris_bufflen(buff)], value, sz);
2375   eris_bufflen(buff) += sz;
2376   return 0;
2377 }
2378 
2379 /** ======================================================================== */
2380 
2381 /* Readonly, interface compatible with MBuffer macros. */
2382 typedef struct RBuffer {
2383   const char *buffer;
2384   size_t n;
2385   size_t buffsize;
2386 } RBuffer;
2387 
2388 static const char*
reader(lua_State * L,void * ud,size_t * sz)2389 reader(lua_State *L, void *ud, size_t *sz) {
2390   RBuffer *buff = (RBuffer*)ud;
2391   (void) L; /* unused */
2392   if (eris_bufflen(buff) == 0) {
2393     return NULL;
2394   }
2395   *sz = eris_bufflen(buff);
2396   eris_bufflen(buff) = 0;
2397   return eris_buffer(buff);
2398 }
2399 
2400 /* }======================================================================== */
2401 
2402 /*
2403 ** {===========================================================================
2404 ** Library functions.
2405 ** ============================================================================
2406 */
2407 
2408 static void
p_header(Info * info)2409 p_header(Info *info) {
2410   WRITE_RAW(kHeader, HEADER_LENGTH);
2411   WRITE_VALUE(sizeof(lua_Number), uint8_t);
2412   WRITE_VALUE(kHeaderNumber, lua_Number);
2413   WRITE_VALUE(sizeof(lua_Integer), uint8_t);
2414   WRITE_VALUE(sizeof(int), uint8_t);
2415   WRITE_VALUE(sizeof(size_t), uint8_t);
2416 }
2417 
2418 static void
u_header(Info * info)2419 u_header(Info *info) {
2420   char header[HEADER_LENGTH];
2421   uint8_t number_size;
2422   READ_RAW(header, HEADER_LENGTH);
2423   if (strncmp(kHeader, header, HEADER_LENGTH)) {
2424     luaL_error(info->L, "invalid data");
2425   }
2426   number_size = READ_VALUE(uint8_t);
2427   if (number_size == 0) {
2428     /* Old 64-bit versions of eris wrote '\0' and then three random bytes. */
2429     /* We skip them here for backwards compatibility. */
2430     char throw_away[3];
2431     READ_RAW(throw_away, 3);
2432 
2433     number_size = READ_VALUE(uint8_t);
2434   }
2435   if (number_size != sizeof(lua_Number)) {
2436     luaL_error(info->L, "incompatible floating point type");
2437   }
2438   /* In this case we really do want floating point equality. */
2439   if (READ_VALUE(lua_Number) != kHeaderNumber) {
2440     luaL_error(info->L, "incompatible floating point representation");
2441   }
2442   if (READ_VALUE(uint8_t) != sizeof(lua_Integer)) {
2443     luaL_error(info->L, "incompatible integer type");
2444   }
2445   info->u.upi.sizeof_int = READ_VALUE(uint8_t);
2446   info->u.upi.sizeof_size_t = READ_VALUE(uint8_t);
2447 }
2448 
2449 static void
unchecked_persist(lua_State * L,lua_Writer writer,void * ud)2450 unchecked_persist(lua_State *L, lua_Writer writer, void *ud) {
2451   Info info;                                            /* perms buff rootobj */
2452   info.L = L;
2453   info.level = 0;
2454   info.refcount = 0;
2455   info.maxComplexity = kMaxComplexity;
2456   info.passIOToPersist = kPassIOToPersist;
2457   info.generatePath = kGeneratePath;
2458   info.u.pi.writer = writer;
2459   info.u.pi.ud = ud;
2460   info.u.pi.metafield = kPersistKey;
2461   info.u.pi.writeDebugInfo = kWriteDebugInformation;
2462 
2463   eris_checkstack(L, 3);
2464 
2465   if (get_setting(L, (void*)&kSettingMaxComplexity)) {
2466                                                   /* perms buff rootobj value */
2467     info.maxComplexity = lua_tointeger(L, -1);
2468     lua_pop(L, 1);                                      /* perms buff rootobj */
2469   }
2470   if (get_setting(L, (void*)&kSettingGeneratePath)) {
2471                                                   /* perms buff rootobj value */
2472     info.generatePath = lua_toboolean(L, -1);
2473     lua_pop(L, 1);                                      /* perms buff rootobj */
2474   }
2475   if (get_setting(L, (void*)&kSettingPassIOToPersist)) {
2476                                                   /* perms buff rootobj value */
2477     info.passIOToPersist = lua_toboolean(L, -1);
2478     lua_pop(L, 1);                                      /* perms buff rootobj */
2479   }
2480   if (get_setting(L, (void*)&kSettingMetafield)) {/* perms buff rootobj value */
2481     info.u.pi.metafield = lua_tostring(L, -1);
2482     lua_pop(L, 1);                                      /* perms buff rootobj */
2483   }
2484   if (get_setting(L, (void*)&kSettingWriteDebugInfo)) {
2485                                                   /* perms buff rootobj value */
2486     info.u.pi.writeDebugInfo = lua_toboolean(L, -1);
2487     lua_pop(L, 1);                                      /* perms buff rootobj */
2488   }
2489 
2490   lua_newtable(L);                               /* perms buff rootobj reftbl */
2491   lua_insert(L, REFTIDX);                        /* perms reftbl buff rootobj */
2492   if (info.generatePath) {
2493     lua_newtable(L);                        /* perms reftbl buff rootobj path */
2494     lua_insert(L, PATHIDX);                 /* perms reftbl buff path rootobj */
2495     pushpath(&info, "root");
2496   }
2497 
2498   /* Populate perms table with Lua internals. */
2499   lua_pushvalue(L, PERMIDX);         /* perms reftbl buff path? rootobj perms */
2500   populateperms(L, false);
2501   lua_pop(L, 1);                           /* perms reftbl buff path? rootobj */
2502 
2503   p_header(&info);
2504   persist(&info);                          /* perms reftbl buff path? rootobj */
2505 
2506   if (info.generatePath) {                  /* perms reftbl buff path rootobj */
2507     lua_remove(L, PATHIDX);                      /* perms reftbl buff rootobj */
2508   }                                              /* perms reftbl buff rootobj */
2509   lua_remove(L, REFTIDX);                               /* perms buff rootobj */
2510 }
2511 
2512 static void
unchecked_unpersist(lua_State * L,lua_Reader reader,void * ud)2513 unchecked_unpersist(lua_State *L, lua_Reader reader, void *ud) {/* perms str? */
2514   Info info;
2515   info.L = L;
2516   info.level = 0;
2517   info.refcount = 0;
2518   info.maxComplexity = kMaxComplexity;
2519   info.generatePath = kGeneratePath;
2520   info.passIOToPersist = kPassIOToPersist;
2521   eris_init(L, &info.u.upi.zio, reader, ud);
2522 
2523   eris_checkstack(L, 3);
2524 
2525   if (get_setting(L, (void*)&kSettingMaxComplexity)) {
2526                                                   /* perms buff rootobj value */
2527     info.maxComplexity = lua_tointeger(L, -1);
2528     lua_pop(L, 1);                                      /* perms buff rootobj */
2529   }
2530   if (get_setting(L, (void*)&kSettingGeneratePath)) {
2531                                                  /* perms buff? rootobj value */
2532     info.generatePath = lua_toboolean(L, -1);
2533     lua_pop(L, 1);                                     /* perms buff? rootobj */
2534   }
2535   if (get_setting(L, (void*)&kSettingPassIOToPersist)) {  /* perms str? value */
2536     info.passIOToPersist = lua_toboolean(L, -1);
2537     lua_pop(L, 1);                                              /* perms str? */
2538   }
2539 
2540   lua_newtable(L);                                       /* perms str? reftbl */
2541   lua_insert(L, REFTIDX);                                /* perms reftbl str? */
2542   if (info.generatePath) {
2543     /* Make sure the path is always at index 4, so that it's the same for
2544      * persist and unpersist. */
2545     lua_pushnil(L);                                  /* perms reftbl str? nil */
2546     lua_insert(L, BUFFIDX);                          /* perms reftbl nil str? */
2547     lua_newtable(L);                            /* perms reftbl nil str? path */
2548     lua_insert(L, PATHIDX);                     /* perms reftbl nil path str? */
2549     pushpath(&info, "root");
2550   }
2551 
2552   /* Populate perms table with Lua internals. */
2553   lua_pushvalue(L, PERMIDX);            /* perms reftbl nil? path? str? perms */
2554   populateperms(L, true);
2555   lua_pop(L, 1);                              /* perms reftbl nil? path? str? */
2556 
2557   u_header(&info);
2558   unpersist(&info);                   /* perms reftbl nil? path? str? rootobj */
2559   if (info.generatePath) {              /* perms reftbl nil path str? rootobj */
2560     lua_remove(L, PATHIDX);                  /* perms reftbl nil str? rootobj */
2561     lua_remove(L, BUFFIDX);                      /* perms reftbl str? rootobj */
2562   }                                              /* perms reftbl str? rootobj */
2563   lua_remove(L, REFTIDX);                               /* perms str? rootobj */
2564 }
2565 
2566 /** ======================================================================== */
2567 
2568 static int
l_persist(lua_State * L)2569 l_persist(lua_State *L) {                             /* perms? rootobj? ...? */
2570   Mbuffer buff;
2571 
2572   /* See if we have anything at all. */
2573   luaL_checkany(L, 1);
2574 
2575   /* If we only have one object we assume it is the root object and that there
2576    * is no perms table, so we create an empty one for internal use. */
2577   if (lua_gettop(L) == 1) {                                        /* rootobj */
2578     eris_checkstack(L, 1);
2579     lua_newtable(L);                                         /* rootobj perms */
2580     lua_insert(L, PERMIDX);                                  /* perms rootobj */
2581   }
2582   else {
2583     luaL_checktype(L, 1, LUA_TTABLE);                  /* perms rootobj? ...? */
2584     luaL_checkany(L, 2);                                /* perms rootobj ...? */
2585     lua_settop(L, 2);                                        /* perms rootobj */
2586   }
2587   eris_checkstack(L, 1);
2588   lua_pushnil(L);                                       /* perms rootobj buff */
2589   lua_insert(L, 2);                                     /* perms buff rootobj */
2590 
2591   eris_initbuffer(L, &buff);
2592   eris_bufflen(&buff) = 0; /* Not initialized by initbuffer... */
2593 
2594   unchecked_persist(L, writer, &buff);                  /* perms buff rootobj */
2595 
2596   /* Copy the buffer as the result string before removing it, to avoid the data
2597    * being garbage collected. */
2598   lua_pushlstring(L, eris_buffer(&buff), eris_bufflen(&buff));
2599                                                     /* perms buff rootobj str */
2600 
2601   return 1;
2602 }
2603 
2604 static int
l_unpersist(lua_State * L)2605 l_unpersist(lua_State *L) {                               /* perms? str? ...? */
2606   RBuffer buff;
2607 
2608   /* See if we have anything at all. */
2609   luaL_checkany(L, 1);
2610 
2611   /* If we only have one object we assume it is the root object and that there
2612    * is no perms table, so we create an empty one for internal use. */
2613   if (lua_gettop(L) == 1) {                                           /* str? */
2614     eris_checkstack(L, 1);
2615     lua_newtable(L);                                            /* str? perms */
2616     lua_insert(L, PERMIDX);                                     /* perms str? */
2617   }
2618   else {
2619     luaL_checktype(L, 1, LUA_TTABLE);                      /* perms str? ...? */
2620   }
2621   eris_buffer(&buff) = luaL_checklstring(L, 2, &eris_bufflen(&buff));
2622   eris_sizebuffer(&buff) = eris_bufflen(&buff);             /* perms str ...? */
2623   lua_settop(L, 2);                                              /* perms str */
2624 
2625   unchecked_unpersist(L, reader, &buff);                 /* perms str rootobj */
2626 
2627   return 1;
2628 }
2629 
2630 #define IS(s) strncmp(s, name, length < sizeof(s) ? length : sizeof(s)) == 0
2631 
2632 static int
l_settings(lua_State * L)2633 l_settings(lua_State *L) {                                /* name value? ...? */
2634   size_t length;
2635   const char *name = luaL_checklstring(L, 1, &length);
2636   if (lua_isnone(L, 2)) {                                        /* name ...? */
2637     lua_settop(L, 1);                                                 /* name */
2638     /* Get the current setting value and return it. */
2639     if (IS(kSettingMetafield)) {
2640       if (!get_setting(L, (void*)&kSettingMetafield)) {
2641         lua_pushstring(L, kPersistKey);
2642       }
2643     }
2644     else if (IS(kSettingPassIOToPersist)) {
2645       if (!get_setting(L, (void*)&kSettingPassIOToPersist)) {
2646         lua_pushboolean(L, kPassIOToPersist);
2647       }
2648     }
2649     else if (IS(kSettingWriteDebugInfo)) {
2650       if (!get_setting(L, (void*)&kSettingWriteDebugInfo)) {
2651         lua_pushboolean(L, kWriteDebugInformation);
2652       }
2653     }
2654     else if (IS(kSettingGeneratePath)) {
2655       if (!get_setting(L, (void*)&kSettingGeneratePath)) {
2656         lua_pushboolean(L, kGeneratePath);
2657       }
2658     }
2659     else if (IS(kSettingMaxComplexity)) {
2660       if (!get_setting(L, (void*)&kSettingMaxComplexity)) {
2661         lua_pushinteger(L, kMaxComplexity);
2662       }
2663     }
2664     else {
2665       return luaL_argerror(L, 1, "no such setting");
2666     }                                                           /* name value */
2667     return 1;
2668   }
2669   else {                                                   /* name value ...? */
2670     lua_settop(L, 2);                                           /* name value */
2671     /* Set a new value for the setting. */
2672     if (IS(kSettingMetafield)) {
2673       luaL_optstring(L, 2, NULL);
2674       set_setting(L, (void*)&kSettingMetafield);
2675     }
2676     else if (IS(kSettingPassIOToPersist)) {
2677       luaL_opt(L, checkboolean, 2, false);
2678       set_setting(L, (void*)&kSettingPassIOToPersist);
2679     }
2680     else if (IS(kSettingWriteDebugInfo)) {
2681       luaL_opt(L, checkboolean, 2, false);
2682       set_setting(L, (void*)&kSettingWriteDebugInfo);
2683     }
2684     else if (IS(kSettingGeneratePath)) {
2685       luaL_opt(L, checkboolean, 2, false);
2686       set_setting(L, (void*)&kSettingGeneratePath);
2687     }
2688     else if (IS(kSettingMaxComplexity)) {
2689       luaL_optinteger(L, 2, 0);
2690       set_setting(L, (void*)&kSettingMaxComplexity);
2691     }
2692     else {
2693       return luaL_argerror(L, 1, "no such setting");
2694     }                                                                 /* name */
2695     return 0;
2696   }
2697 }
2698 
2699 #undef IS
2700 
2701 /** ======================================================================== */
2702 
2703 static luaL_Reg erislib[] = {
2704   { "persist", l_persist },
2705   { "unpersist", l_unpersist },
2706   { "settings", l_settings },
2707   { NULL, NULL }
2708 };
2709 
luaopen_eris(lua_State * L)2710 LUA_API int luaopen_eris(lua_State *L) {
2711   luaL_newlib(L, erislib);
2712   return 1;
2713 }
2714 
2715 /* }======================================================================== */
2716 
2717 /*
2718 ** {===========================================================================
2719 ** Public API functions.
2720 ** ============================================================================
2721 */
2722 
2723 LUA_API void
eris_dump(lua_State * L,lua_Writer writer,void * ud)2724 eris_dump(lua_State *L, lua_Writer writer, void *ud) {     /* perms? rootobj? */
2725   if (lua_gettop(L) > 2) {
2726     luaL_error(L, "too many arguments");
2727   }
2728   luaL_checktype(L, 1, LUA_TTABLE);                         /* perms rootobj? */
2729   luaL_checkany(L, 2);                                       /* perms rootobj */
2730   lua_pushnil(L);                                        /* perms rootobj nil */
2731   lua_insert(L, -2);                                     /* perms nil rootobj */
2732   unchecked_persist(L, writer, ud);                      /* perms nil rootobj */
2733   lua_remove(L, -2);                                         /* perms rootobj */
2734 }
2735 
2736 LUA_API void
eris_undump(lua_State * L,lua_Reader reader,void * ud)2737 eris_undump(lua_State *L, lua_Reader reader, void *ud) {            /* perms? */
2738   if (lua_gettop(L) > 1) {
2739     luaL_error(L, "too many arguments");
2740   }
2741   luaL_checktype(L, 1, LUA_TTABLE);                                  /* perms */
2742   unchecked_unpersist(L, reader, ud);                        /* perms rootobj */
2743 }
2744 
2745 /** ======================================================================== */
2746 
2747 LUA_API void
eris_persist(lua_State * L,int perms,int value)2748 eris_persist(lua_State *L, int perms, int value) {                    /* ...? */
2749   perms = lua_absindex(L, perms);
2750   value = lua_absindex(L, value);
2751   eris_checkstack(L, 3);
2752   lua_pushcfunction(L, l_persist);                           /* ... l_persist */
2753   lua_pushvalue(L, perms);                             /* ... l_persist perms */
2754   lua_pushvalue(L, value);                     /* ... l_persist perms rootobj */
2755   lua_call(L, 2, 1);                                               /* ... str */
2756 }
2757 
2758 LUA_API void
eris_unpersist(lua_State * L,int perms,int value)2759 eris_unpersist(lua_State *L, int perms, int value) {                   /* ... */
2760   perms = lua_absindex(L, perms);
2761   value = lua_absindex(L, value);
2762   eris_checkstack(L, 3);
2763   lua_pushcfunction(L, l_unpersist);                       /* ... l_unpersist */
2764   lua_pushvalue(L, perms);                           /* ... l_unpersist perms */
2765   lua_pushvalue(L, value);                       /* ... l_unpersist perms str */
2766   lua_call(L, 2, 1);                                           /* ... rootobj */
2767 }
2768 
2769 LUA_API void
eris_get_setting(lua_State * L,const char * name)2770 eris_get_setting(lua_State *L, const char *name) {                     /* ... */
2771   eris_checkstack(L, 2);
2772   lua_pushcfunction(L, l_settings);                         /* ... l_settings */
2773   lua_pushstring(L, name);                             /* ... l_settings name */
2774   lua_call(L, 1, 1);                                             /* ... value */
2775 }
2776 
2777 LUA_API void
eris_set_setting(lua_State * L,const char * name,int value)2778 eris_set_setting(lua_State *L, const char *name, int value) {          /* ... */
2779   value = lua_absindex(L, value);
2780   eris_checkstack(L, 3);
2781   lua_pushcfunction(L, l_settings);                         /* ... l_settings */
2782   lua_pushstring(L, name);                             /* ... l_settings name */
2783   lua_pushvalue(L, value);                       /* ... l_settings name value */
2784   lua_call(L, 2, 0);                                                   /* ... */
2785 }
2786 
2787 /* }======================================================================== */
2788 
2789