240 lines
9.0 KiB
Plaintext
240 lines
9.0 KiB
Plaintext
/*
|
|
The format is largely equal to that of Pluto, with some changes to reduce
|
|
file size, and where necessary due to changes in Lua's architecture.
|
|
|
|
Pseudo-C is used to express the file format. Padding is assumed to be
|
|
nonexistent. The keyword "one_of" is used to express a concept similar to
|
|
"union", except that its size is the size of the actual datatype chosen. Thus,
|
|
objects which contain, directly or indirectly, a one_of, may vary in size. The
|
|
keyword "if" is used to express the fact that the following block may or may
|
|
not be present, usually indicated by the type of a previous "one_of".
|
|
*/
|
|
|
|
struct PersistedData {
|
|
Header header; /* The header used for basic validation. */
|
|
Object rootobj; /* The root object that was persisted. */
|
|
};
|
|
|
|
struct Header {
|
|
char header[4] = "ERIS"; /* Header signature for rudimentary validation */
|
|
uint8_t sizeof_number; /* sizeof(lua_Number) to check type compatibility */
|
|
lua_Number test; /* -1.234567890 to check representation compatibility */
|
|
uint8_t sizeof_int; /* sizeof(int) in persisted data */
|
|
uint8_t sizeof_size_t; /* sizeof(size_t) in persisted data */
|
|
/* Note that the last two fields determine the size of the int and size_t
|
|
* fields in the following definitions. We write each value in the native
|
|
* "size" and check for truncation when reading, if necessary. */
|
|
};
|
|
|
|
struct Object {
|
|
one_of {
|
|
int type; /* if the value is one of LUA_TXXX or ERIS_PERMANENT */
|
|
Reference r; /* otherwise */
|
|
/* Note that the types LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER and
|
|
* LUA_TLIGHTUSERDATA will never be "referenced", but always be written
|
|
* directly. */
|
|
}
|
|
if (type) {
|
|
RealObject o; /* if we have a type, not a reference */
|
|
/* If the object is not primitive (see list above) we remember it and
|
|
* increment the reference counter, and point any future occurrences of
|
|
* it to this one via a reference (see above, Reference r). */
|
|
}
|
|
};
|
|
|
|
struct Reference {
|
|
int ref; /* The index the object was registered with */
|
|
};
|
|
|
|
struct RealObject {
|
|
one_of {
|
|
uint8_t b; /* If type == LUA_TBOOLEAN */
|
|
size_t l; /* If type == LUA_TLIGHTUSERDATA */
|
|
Number n; /* If type == LUA_TNUMBER */
|
|
String s; /* If type == LUA_TSTRING */
|
|
Table t; /* If type == LUA_TTABLE */
|
|
Closure f; /* If type == LUA_TFUNCTION */
|
|
Userdata u; /* If type == LUA_TUSERDATA */
|
|
Thread th; /* If type == LUA_TTHREAD */
|
|
Proto p; /* If type == LUA_TPROTO (from lobject.h) */
|
|
UpVal uv; /* If type == LUA_TUPVAL (from lobject.h) */
|
|
PermKey pk; /* if type == ERIS_PERMANENT */
|
|
};
|
|
};
|
|
|
|
struct Number {
|
|
if(sizeof(lua_Number) == sizeof(uint32_t)) {
|
|
uint32_t rep; /* binary representation of the number, stored as
|
|
* integer to re-use endian-agnosticism */
|
|
}
|
|
else if (sizeof(lua_Number) == sizeof(uint64_t)) {
|
|
uint64_t rep; /* binary representation of the number, stored as
|
|
* integer to re-use endian-agnosticism */
|
|
}
|
|
else {
|
|
/* unsupported float type -- error if asserts are enabled */
|
|
}
|
|
};
|
|
|
|
struct String {
|
|
size_t length; /* The length of the string */
|
|
char str[length]; /* The actual string (not always null terminated) */
|
|
};
|
|
|
|
struct Table {
|
|
uint8_t isSpecial; /* 1 if SP is used; 0 otherwise */
|
|
one_of {
|
|
Object c; /* if isspecial == 1; closure to refill the table */
|
|
LiteralTable t; /* if isspecial == 0; literal table info */
|
|
};
|
|
};
|
|
|
|
struct LiteralTable {
|
|
Pair p[]; /* key/value pairs */
|
|
Object nil = nil; /* Nil reference to terminate */
|
|
Object metatable; /* The metatable (nil for none, otherwise LUA_TTABLE) */
|
|
};
|
|
|
|
struct Pair {
|
|
Object key; /* never nil, since that indicates the end */
|
|
Object value; /* never nil, since the entry wouldn't exist then */
|
|
};
|
|
|
|
struct Userdata {
|
|
uint8_t isSpecial; /* 1 for special persistence, 0 for literal */
|
|
one_of {
|
|
Object c; /* if isspecial == 1; closure to recreate the udata */
|
|
LiteralUserdata lu; /* if is_special is 0 */
|
|
};
|
|
};
|
|
|
|
struct LiteralUserdata {
|
|
size_t length; /* Size of the data */
|
|
char data[length]; /* The actual data */
|
|
Object metatable; /* The metatable (nil for none, otherwise LUA_TTABLE) */
|
|
};
|
|
|
|
struct Closure {
|
|
uint8_t isCClosure; /* 1 if the closure is a C closure; 0 otherwise */
|
|
uint8_t nups; /* Number of upvalues the function uses */
|
|
one_of {
|
|
CClosure ccl; /* if isCClosure == 1 */
|
|
LClosure lcl; /* if isCClosure == 0; it's a Lua closure */
|
|
};
|
|
};
|
|
|
|
struct CClosure {
|
|
Object f; /* The actual C function. Must be available via the
|
|
* permanents table on persist and unpersist. */
|
|
Object upvals[Closure.nups]; /* All upvalues */
|
|
/* Note that here the upvalues are the actual objects, i.e. these are not
|
|
* of type LUA_TUPVAL, since C closures' upalues are always closed. */
|
|
};
|
|
|
|
struct LClosure {
|
|
Object proto; /* The proto this function uses */
|
|
Object upvals[Closure.nups]; /* All upvalues */
|
|
};
|
|
|
|
struct UpVal {
|
|
Object obj; /* The object this upval refers to; we proxy it with
|
|
* the LUA_TUPVAL type to keep shared upvalues intact */
|
|
}
|
|
|
|
struct Proto {
|
|
int linedefined; /* Start of line range */
|
|
int lastlinedefined; /* End of line range */
|
|
uint8_t numparams; /* Number of parameters taken */
|
|
uint8_t is_vararg; /* 1 if function accepts varargs, 0 otherwise */
|
|
uint8_t maxstacksize; /* Size of stack reserved for the function */
|
|
|
|
int sizecode; /* Number of instructions in code */
|
|
Instruction code[sizecode]; /* The proto's code */
|
|
|
|
int sizek; /* Number of constants referenced */
|
|
Object k[sizek]; /* Constants referenced */
|
|
|
|
int sizep; /* Number of inner Protos referenced */
|
|
Object p[sizep]; /* Inner Protos referenced */
|
|
|
|
int sizeupvalues; /* Number of upvalues used */
|
|
Upvaldesc upvalues[sizeupvalues]; /* The locations of the upvalues */
|
|
|
|
uint8_t debug; /* 1 if debug data is present; 0 otherwise */
|
|
if (debug) {
|
|
Object source; /* The source code string */
|
|
|
|
int sizelineinfo; /* Number of opcode-line mappings */
|
|
int lineinfo[sizelineinfo]; /* opcode-line mappings */
|
|
|
|
int sizelocvars; /* Number of local variable names */
|
|
LocVar[sizelocvars]; /* Local variable names */
|
|
|
|
Object upvalnames[sizeupvalues]; /* Upvalue names */
|
|
}
|
|
};
|
|
|
|
struct Upvaldesc {
|
|
uint8_t instack; /* whether it is in stack */
|
|
uint8_t idx; /* index of upvalue (in stack or in outer function's list) */
|
|
};
|
|
|
|
struct LocVar {
|
|
int startpc; /* Point where variable is active */
|
|
int endpc; /* Point where variable is dead */
|
|
Object name; /* Name of the local variable */
|
|
};
|
|
|
|
struct Thread {
|
|
int stacksize; /* The overall size of the stack filled with objects,
|
|
* including all stack frames. */
|
|
size_t top; /* top = L->top - L->stack; */
|
|
Object stack[stacksize]; /* All stack values, bottom up */
|
|
|
|
uint8_t status; /* current thread status (ok, yield) */
|
|
size_t errfunc; /* current error handling function (stack index) */
|
|
|
|
CallInfo ci[]; /* The CallInfo stack, starting with base_ci */
|
|
if (status == LUA_YIELD) {
|
|
size_t extra; /* value of thread->ci->extra, which is the original
|
|
* value of thread->ci->func */
|
|
}
|
|
|
|
OpenUpval openupvals[]; /* Upvalues to open */
|
|
};
|
|
|
|
struct CallInfo {
|
|
size_t func; /* func = ci->func - thread->stack */
|
|
size_t top; /* top = ci->top - thread->stack */
|
|
int16_t nresults; /* expected number of results from this function */
|
|
uint8_t callstatus;
|
|
if (callstatus & CIST_YPCALL) {
|
|
size_t extra; /* the stack level of the function being pcalled */
|
|
}
|
|
if (callstatus & CIST_LUA) {
|
|
size_t base; /* base = ci->u.l.base - thread-stack */
|
|
size_t savedpc; /* savedpc = ci->u.l.savedpc - ci_func(ci)->p->code */
|
|
}
|
|
else {
|
|
uint8_t status;
|
|
if (callstatus & (CIST_YPCALL | CIST_YIELDED)) {
|
|
int ctx; /* context info. in case of yields */
|
|
Object k; /* C function, callback for resuming */
|
|
}
|
|
}
|
|
uint8_t hasNext; /* 1 if there's another CI to read; 0 otherwise */
|
|
};
|
|
|
|
struct OpenUpval {
|
|
size_t idx; /* stack index of the value + 1; 0 if end of list */
|
|
Object upval; /* The upvalue */
|
|
};
|
|
|
|
struct PermKey {
|
|
uint8_t type; /* The actual LUA_TXXX of the original value. */
|
|
Object key; /* The value to use as a key when unpersisting. */
|
|
/* Note that we store the type of the original value (replaced by the
|
|
* permanent table value used as a key when unpersisting) to ensure the
|
|
* value in the permanents table when unpersisting has the correct type. */
|
|
};
|