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