mirror of
https://github.com/lua/lua.git
synced 2025-01-28 06:03:00 +08:00
19afd91687
The use of tables in dump/undump to reuse strings did not exist in the version that changed the representation of arrays, so it was not corrected for the new API for tables.
394 lines
9.4 KiB
C
394 lines
9.4 KiB
C
/*
|
|
** $Id: lundump.c $
|
|
** load precompiled Lua chunks
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#define lundump_c
|
|
#define LUA_CORE
|
|
|
|
#include "lprefix.h"
|
|
|
|
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
#include "lua.h"
|
|
|
|
#include "ldebug.h"
|
|
#include "ldo.h"
|
|
#include "lfunc.h"
|
|
#include "lmem.h"
|
|
#include "lobject.h"
|
|
#include "lstring.h"
|
|
#include "ltable.h"
|
|
#include "lundump.h"
|
|
#include "lzio.h"
|
|
|
|
|
|
#if !defined(luai_verifycode)
|
|
#define luai_verifycode(L,f) /* empty */
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
lua_State *L;
|
|
ZIO *Z;
|
|
const char *name;
|
|
Table *h; /* list for string reuse */
|
|
lu_mem offset; /* current position relative to beginning of dump */
|
|
lua_Integer nstr; /* number of strings in the list */
|
|
lu_byte fixed; /* dump is fixed in memory */
|
|
} LoadState;
|
|
|
|
|
|
static l_noret error (LoadState *S, const char *why) {
|
|
luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why);
|
|
luaD_throw(S->L, LUA_ERRSYNTAX);
|
|
}
|
|
|
|
|
|
/*
|
|
** All high-level loads go through loadVector; you can change it to
|
|
** adapt to the endianness of the input
|
|
*/
|
|
#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0]))
|
|
|
|
static void loadBlock (LoadState *S, void *b, size_t size) {
|
|
if (luaZ_read(S->Z, b, size) != 0)
|
|
error(S, "truncated chunk");
|
|
S->offset += size;
|
|
}
|
|
|
|
|
|
static void loadAlign (LoadState *S, int align) {
|
|
int padding = align - (S->offset % align);
|
|
if (padding < align) { /* apd == align means no padding */
|
|
lua_Integer paddingContent;
|
|
loadBlock(S, &paddingContent, padding);
|
|
lua_assert(S->offset % align == 0);
|
|
}
|
|
}
|
|
|
|
|
|
#define getaddr(S,n,t) cast(t *, getaddr_(S,n,sizeof(t)))
|
|
|
|
static const void *getaddr_ (LoadState *S, int n, int sz) {
|
|
const void *block = luaZ_getaddr(S->Z, n * sz);
|
|
if (block == NULL)
|
|
error(S, "truncated fixed buffer");
|
|
return block;
|
|
}
|
|
|
|
|
|
#define loadVar(S,x) loadVector(S,&x,1)
|
|
|
|
|
|
static lu_byte loadByte (LoadState *S) {
|
|
int b = zgetc(S->Z);
|
|
if (b == EOZ)
|
|
error(S, "truncated chunk");
|
|
S->offset++;
|
|
return cast_byte(b);
|
|
}
|
|
|
|
|
|
static size_t loadUnsigned (LoadState *S, size_t limit) {
|
|
size_t x = 0;
|
|
int b;
|
|
limit >>= 7;
|
|
do {
|
|
b = loadByte(S);
|
|
if (x >= limit)
|
|
error(S, "integer overflow");
|
|
x = (x << 7) | (b & 0x7f);
|
|
} while ((b & 0x80) == 0);
|
|
return x;
|
|
}
|
|
|
|
|
|
static size_t loadSize (LoadState *S) {
|
|
return loadUnsigned(S, MAX_SIZET);
|
|
}
|
|
|
|
|
|
static int loadInt (LoadState *S) {
|
|
return cast_int(loadUnsigned(S, INT_MAX));
|
|
}
|
|
|
|
|
|
static lua_Number loadNumber (LoadState *S) {
|
|
lua_Number x;
|
|
loadVar(S, x);
|
|
return x;
|
|
}
|
|
|
|
|
|
static lua_Integer loadInteger (LoadState *S) {
|
|
lua_Integer x;
|
|
loadVar(S, x);
|
|
return x;
|
|
}
|
|
|
|
|
|
/*
|
|
** Load a nullable string into prototype 'p'.
|
|
*/
|
|
static TString *loadStringN (LoadState *S, Proto *p) {
|
|
lua_State *L = S->L;
|
|
TString *ts;
|
|
TValue sv;
|
|
size_t size = loadSize(S);
|
|
if (size == 0) /* no string? */
|
|
return NULL;
|
|
else if (size == 1) { /* previously saved string? */
|
|
int idx = loadInt(S); /* get its index */
|
|
TValue stv;
|
|
luaH_getint(S->h, idx, &stv);
|
|
return tsvalue(&stv);
|
|
}
|
|
else if (size -= 2, size <= LUAI_MAXSHORTLEN) { /* short string? */
|
|
char buff[LUAI_MAXSHORTLEN];
|
|
loadVector(S, buff, size); /* load string into buffer */
|
|
ts = luaS_newlstr(L, buff, size); /* create string */
|
|
}
|
|
else { /* long string */
|
|
ts = luaS_createlngstrobj(L, size); /* create string */
|
|
setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
|
|
luaD_inctop(L);
|
|
loadVector(S, getlngstr(ts), size); /* load directly in final place */
|
|
L->top.p--; /* pop string */
|
|
}
|
|
luaC_objbarrier(L, p, ts);
|
|
S->nstr++; /* add string to list of saved strings */
|
|
setsvalue(L, &sv, ts);
|
|
luaH_setint(L, S->h, S->nstr, &sv);
|
|
luaC_objbarrierback(L, obj2gco(S->h), ts);
|
|
return ts;
|
|
}
|
|
|
|
|
|
/*
|
|
** Load a non-nullable string into prototype 'p'.
|
|
*/
|
|
static TString *loadString (LoadState *S, Proto *p) {
|
|
TString *st = loadStringN(S, p);
|
|
if (st == NULL)
|
|
error(S, "bad format for constant string");
|
|
return st;
|
|
}
|
|
|
|
|
|
static void loadCode (LoadState *S, Proto *f) {
|
|
int n = loadInt(S);
|
|
loadAlign(S, sizeof(f->code[0]));
|
|
if (S->fixed) {
|
|
f->code = getaddr(S, n, Instruction);
|
|
f->sizecode = n;
|
|
}
|
|
else {
|
|
f->code = luaM_newvectorchecked(S->L, n, Instruction);
|
|
f->sizecode = n;
|
|
loadVector(S, f->code, n);
|
|
}
|
|
}
|
|
|
|
|
|
static void loadFunction(LoadState *S, Proto *f);
|
|
|
|
|
|
static void loadConstants (LoadState *S, Proto *f) {
|
|
int i;
|
|
int n = loadInt(S);
|
|
f->k = luaM_newvectorchecked(S->L, n, TValue);
|
|
f->sizek = n;
|
|
for (i = 0; i < n; i++)
|
|
setnilvalue(&f->k[i]);
|
|
for (i = 0; i < n; i++) {
|
|
TValue *o = &f->k[i];
|
|
int t = loadByte(S);
|
|
switch (t) {
|
|
case LUA_VNIL:
|
|
setnilvalue(o);
|
|
break;
|
|
case LUA_VFALSE:
|
|
setbfvalue(o);
|
|
break;
|
|
case LUA_VTRUE:
|
|
setbtvalue(o);
|
|
break;
|
|
case LUA_VNUMFLT:
|
|
setfltvalue(o, loadNumber(S));
|
|
break;
|
|
case LUA_VNUMINT:
|
|
setivalue(o, loadInteger(S));
|
|
break;
|
|
case LUA_VSHRSTR:
|
|
case LUA_VLNGSTR:
|
|
setsvalue2n(S->L, o, loadString(S, f));
|
|
break;
|
|
default: lua_assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void loadProtos (LoadState *S, Proto *f) {
|
|
int i;
|
|
int n = loadInt(S);
|
|
f->p = luaM_newvectorchecked(S->L, n, Proto *);
|
|
f->sizep = n;
|
|
for (i = 0; i < n; i++)
|
|
f->p[i] = NULL;
|
|
for (i = 0; i < n; i++) {
|
|
f->p[i] = luaF_newproto(S->L);
|
|
luaC_objbarrier(S->L, f, f->p[i]);
|
|
loadFunction(S, f->p[i]);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Load the upvalues for a function. The names must be filled first,
|
|
** because the filling of the other fields can raise read errors and
|
|
** the creation of the error message can call an emergency collection;
|
|
** in that case all prototypes must be consistent for the GC.
|
|
*/
|
|
static void loadUpvalues (LoadState *S, Proto *f) {
|
|
int i, n;
|
|
n = loadInt(S);
|
|
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
|
f->sizeupvalues = n;
|
|
for (i = 0; i < n; i++) /* make array valid for GC */
|
|
f->upvalues[i].name = NULL;
|
|
for (i = 0; i < n; i++) { /* following calls can raise errors */
|
|
f->upvalues[i].instack = loadByte(S);
|
|
f->upvalues[i].idx = loadByte(S);
|
|
f->upvalues[i].kind = loadByte(S);
|
|
}
|
|
}
|
|
|
|
|
|
static void loadDebug (LoadState *S, Proto *f) {
|
|
int i, n;
|
|
n = loadInt(S);
|
|
if (S->fixed) {
|
|
f->lineinfo = getaddr(S, n, ls_byte);
|
|
f->sizelineinfo = n;
|
|
}
|
|
else {
|
|
f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
|
|
f->sizelineinfo = n;
|
|
loadVector(S, f->lineinfo, n);
|
|
}
|
|
n = loadInt(S);
|
|
f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
|
|
f->sizeabslineinfo = n;
|
|
for (i = 0; i < n; i++) {
|
|
f->abslineinfo[i].pc = loadInt(S);
|
|
f->abslineinfo[i].line = loadInt(S);
|
|
}
|
|
n = loadInt(S);
|
|
f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
|
|
f->sizelocvars = n;
|
|
for (i = 0; i < n; i++)
|
|
f->locvars[i].varname = NULL;
|
|
for (i = 0; i < n; i++) {
|
|
f->locvars[i].varname = loadStringN(S, f);
|
|
f->locvars[i].startpc = loadInt(S);
|
|
f->locvars[i].endpc = loadInt(S);
|
|
}
|
|
n = loadInt(S);
|
|
if (n != 0) /* does it have debug information? */
|
|
n = f->sizeupvalues; /* must be this many */
|
|
for (i = 0; i < n; i++)
|
|
f->upvalues[i].name = loadStringN(S, f);
|
|
}
|
|
|
|
|
|
static void loadFunction (LoadState *S, Proto *f) {
|
|
f->source = loadStringN(S, f);
|
|
f->linedefined = loadInt(S);
|
|
f->lastlinedefined = loadInt(S);
|
|
f->numparams = loadByte(S);
|
|
f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */
|
|
if (S->fixed)
|
|
f->flag |= PF_FIXED; /* signal that code is fixed */
|
|
f->maxstacksize = loadByte(S);
|
|
loadCode(S, f);
|
|
loadConstants(S, f);
|
|
loadUpvalues(S, f);
|
|
loadProtos(S, f);
|
|
loadDebug(S, f);
|
|
}
|
|
|
|
|
|
static void checkliteral (LoadState *S, const char *s, const char *msg) {
|
|
char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
|
|
size_t len = strlen(s);
|
|
loadVector(S, buff, len);
|
|
if (memcmp(s, buff, len) != 0)
|
|
error(S, msg);
|
|
}
|
|
|
|
|
|
static void fchecksize (LoadState *S, size_t size, const char *tname) {
|
|
if (loadByte(S) != size)
|
|
error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
|
|
}
|
|
|
|
|
|
#define checksize(S,t) fchecksize(S,sizeof(t),#t)
|
|
|
|
static void checkHeader (LoadState *S) {
|
|
/* skip 1st char (already read and checked) */
|
|
checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
|
|
if (loadByte(S) != LUAC_VERSION)
|
|
error(S, "version mismatch");
|
|
if (loadByte(S) != LUAC_FORMAT)
|
|
error(S, "format mismatch");
|
|
checkliteral(S, LUAC_DATA, "corrupted chunk");
|
|
checksize(S, Instruction);
|
|
checksize(S, lua_Integer);
|
|
checksize(S, lua_Number);
|
|
if (loadInteger(S) != LUAC_INT)
|
|
error(S, "integer format mismatch");
|
|
if (loadNumber(S) != LUAC_NUM)
|
|
error(S, "float format mismatch");
|
|
}
|
|
|
|
|
|
/*
|
|
** Load precompiled chunk.
|
|
*/
|
|
LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) {
|
|
LoadState S;
|
|
LClosure *cl;
|
|
if (*name == '@' || *name == '=')
|
|
S.name = name + 1;
|
|
else if (*name == LUA_SIGNATURE[0])
|
|
S.name = "binary string";
|
|
else
|
|
S.name = name;
|
|
S.L = L;
|
|
S.Z = Z;
|
|
S.fixed = fixed;
|
|
S.offset = 1; /* fist byte was already read */
|
|
checkHeader(&S);
|
|
cl = luaF_newLclosure(L, loadByte(&S));
|
|
setclLvalue2s(L, L->top.p, cl);
|
|
luaD_inctop(L);
|
|
S.h = luaH_new(L); /* create list of saved strings */
|
|
S.nstr = 0;
|
|
sethvalue2s(L, L->top.p, S.h); /* anchor it */
|
|
luaD_inctop(L);
|
|
cl->p = luaF_newproto(L);
|
|
luaC_objbarrier(L, cl, cl->p);
|
|
loadFunction(&S, cl->p);
|
|
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
|
luai_verifycode(L, cl->p);
|
|
L->top.p--; /* pop table */
|
|
return cl;
|
|
}
|
|
|