Cleaner protocol between 'lua_dump' and writer function

'lua_dump' signals to the writer function the end of a dump, so that
is has more freedom when using the stack.
This commit is contained in:
Roberto Ierusalimschy 2023-12-14 11:41:57 -03:00
parent ad73b33224
commit 4eda1acafa
6 changed files with 66 additions and 46 deletions

32
lapi.c
View File

@ -1116,36 +1116,18 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
/*
** Dump a function, calling 'writer' to write its parts. Because the
** writer can use the stack in unkown ways, this function should not
** push things on the stack, but it must anchor an auxiliary table
** used by 'luaU_dump'. To do so, it creates the table, anchors the
** function that is on the stack in the table, and substitutes the
** table for the function in the stack.
** Dump a Lua function, calling 'writer' to write its parts. Ensure
** the stack returns with its original size.
*/
LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
int status;
StkId fstk; /* pointer to function */
TValue *o;
ptrdiff_t otop = savestack(L, L->top.p); /* original top */
TValue *f = s2v(L->top.p - 1); /* function to be dumped */
lua_lock(L);
api_checknelems(L, 1);
fstk = L->top.p - 1;
o = s2v(fstk);
if (!isLfunction(o))
status = 1;
else {
LClosure *f = clLvalue(o);
ptrdiff_t fidx = savestack(L, fstk); /* function index */
Table *h = luaH_new(L); /* auxiliary table used by 'luaU_dump' */
sethvalue2s(L, L->top.p, h); /* anchor it (luaH_set may call GC) */
L->top.p++; /* (assume extra slot) */
luaH_set(L, h, o, o); /* anchor function into table */
setobjs2s(L, fstk, L->top.p - 1); /* move table over function */
L->top.p--; /* stack back to initial size */
status = luaU_dump(L, f->p, writer, data, strip, h);
setclLvalue2s(L, restorestack(L, fidx), f); /* put function back */
}
api_check(L, isLfunction(f), "Lua function expected");
status = luaU_dump(L, clLvalue(f)->p, writer, data, strip);
L->top.p = restorestack(L, otop); /* restore top */
lua_unlock(L);
return status;
}

30
ldump.c
View File

@ -43,8 +43,13 @@ typedef struct {
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
/*
** Dump the block of memory pointed by 'b' with given 'size'.
** 'b' should not be NULL, except for the last call signaling the end
** of the dump.
*/
static void dumpBlock (DumpState *D, const void *b, size_t size) {
if (D->status == 0 && size > 0) {
if (D->status == 0) { /* do not write anything after an error */
lua_unlock(D->L);
D->status = (*D->writer)(D->L, b, size, D->data);
lua_lock(D->L);
@ -53,13 +58,18 @@ static void dumpBlock (DumpState *D, const void *b, size_t size) {
}
/*
** Dump enough zeros to ensure that current position is a multiple of
** 'align'.
*/
static void dumpAlign (DumpState *D, int align) {
int padding = align - (D->offset % align);
if (padding < align) { /* apd == align means no padding */
if (padding < align) { /* padding == align means no padding */
static lua_Integer paddingContent = 0;
lua_assert(cast_uint(align) <= sizeof(lua_Integer));
dumpBlock(D, &paddingContent, padding);
lua_assert(D->offset % align == 0);
}
lua_assert(D->offset % align == 0);
}
@ -91,6 +101,7 @@ static void dumpSize (DumpState *D, size_t x) {
static void dumpInt (DumpState *D, int x) {
lua_assert(x >= 0);
dumpSize(D, x);
}
@ -140,6 +151,7 @@ static void dumpString (DumpState *D, TString *ts) {
static void dumpCode (DumpState *D, const Proto *f) {
dumpInt(D, f->sizecode);
dumpAlign(D, sizeof(f->code[0]));
lua_assert(f->code != NULL);
dumpVector(D, f->code, f->sizecode);
}
@ -196,7 +208,8 @@ static void dumpDebug (DumpState *D, const Proto *f) {
int i, n;
n = (D->strip) ? 0 : f->sizelineinfo;
dumpInt(D, n);
dumpVector(D, f->lineinfo, n);
if (f->lineinfo != NULL)
dumpVector(D, f->lineinfo, n);
n = (D->strip) ? 0 : f->sizeabslineinfo;
dumpInt(D, n);
for (i = 0; i < n; i++) {
@ -248,20 +261,23 @@ static void dumpHeader (DumpState *D) {
/*
** dump Lua function as precompiled chunk
*/
int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
int strip, Table *h) {
int luaU_dump (lua_State *L, const Proto *f, lua_Writer w, void *data,
int strip) {
DumpState D;
D.h = luaH_new(L); /* aux. table to keep strings already dumped */
sethvalue2s(L, L->top.p, D.h); /* anchor it */
L->top.p++;
D.L = L;
D.writer = w;
D.offset = 0;
D.data = data;
D.strip = strip;
D.status = 0;
D.h = h;
D.nstr = 0;
dumpHeader(&D);
dumpByte(&D, f->sizeupvalues);
dumpFunction(&D, f);
dumpBlock(&D, NULL, 0); /* signal end of dump */
return D.status;
}

View File

@ -225,7 +225,12 @@ static int writer (lua_State *L, const void *b, size_t size, void *ud) {
state->init = 1;
luaL_buffinit(L, &state->B);
}
luaL_addlstring(&state->B, (const char *)b, size);
if (b == NULL) { /* finishing dump? */
luaL_pushresult(&state->B); /* push result */
lua_replace(L, 1); /* move it to reserved slot */
}
else
luaL_addlstring(&state->B, (const char *)b, size);
return 0;
}
@ -233,13 +238,13 @@ static int writer (lua_State *L, const void *b, size_t size, void *ud) {
static int str_dump (lua_State *L) {
struct str_Writer state;
int strip = lua_toboolean(L, 2);
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 1); /* ensure function is on the top of the stack */
luaL_argcheck(L, lua_type(L, 1) == LUA_TFUNCTION && !lua_iscfunction(L, 1),
1, "Lua function expected");
/* ensure function is on the top of the stack and vacate slot 1 */
lua_pushvalue(L, 1);
state.init = 0;
if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
return luaL_error(L, "unable to dump given function");
luaL_pushresult(&state.B);
lua_assert(lua_isfunction(L, 1)); /* lua_dump kept that value */
lua_dump(L, writer, &state, strip);
lua_settop(L, 1); /* leave final result on top */
return 1;
}

View File

@ -152,7 +152,7 @@ static void loadString (LoadState *S, Proto *p, TString **sl) {
luaH_getint(S->h, idx, &stv);
*sl = ts = tsvalue(&stv);
luaC_objbarrier(L, p, ts);
return;
return; /* do not save it again */
}
else if ((size -= 2) <= LUAI_MAXSHORTLEN) { /* short string? */
char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */
@ -168,10 +168,10 @@ static void loadString (LoadState *S, Proto *p, TString **sl) {
else { /* create internal copy */
*sl = ts = luaS_createlngstrobj(L, size); /* create string */
luaC_objbarrier(L, p, ts);
loadVector(S, getlngstr(ts), size); /* load directly in final place */
loadByte(S); /* skip ending '\0' */
loadVector(S, getlngstr(ts), size + 1); /* load directly in final place */
}
S->nstr++; /* add string to list of saved strings */
/* add string to list of saved strings */
S->nstr++;
setsvalue(L, &sv, ts);
luaH_setint(L, S->h, S->nstr, &sv);
luaC_objbarrierback(L, obj2gco(S->h), ts);

View File

@ -31,6 +31,6 @@ LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name,
/* dump one chunk; from ldump.c */
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
void* data, int strip, Table *h);
void* data, int strip);
#endif

View File

@ -3266,6 +3266,13 @@ As it produces parts of the chunk,
with the given @id{data}
to write them.
The function @Lid{lua_dump} fully preserves the Lua stack
through the calls to the writer function,
except that it may push some values for internal use
before the first call,
and it restores the stack size to its original size
after the last call.
If @id{strip} is true,
the binary representation may not include all debug information
about the function,
@ -3275,8 +3282,6 @@ The value returned is the error code returned by the last
call to the writer;
@N{0 means} no errors.
This function does not pop the Lua function from the stack.
}
@APIEntry{int lua_error (lua_State *L);|
@ -4688,6 +4693,10 @@ passing along the buffer to be written (@id{p}),
its size (@id{sz}),
and the @id{ud} parameter supplied to @Lid{lua_dump}.
After @Lid{lua_dump} writes its last piece,
it will signal that by calling the writer function one more time,
with a @id{NULL} buffer (and size 0).
The writer returns an error code:
@N{0 means} no errors;
any other value means an error and stops @Lid{lua_dump} from
@ -9259,6 +9268,14 @@ it is equivalent to @Lid{lua_closethread} with
@id{from} being @id{NULL}.
}
@item{
The function @Lid{lua_dump} changed the way it keeps the stack
through the calls to the writer function.
(That was not specified in previous versions.)
Also, it calls the writer function one extra time,
to signal the end of the dump.
}
@item{
There were several changes in the parameters
for the options @Lid{LUA_GCINC} and @Lid{LUA_GCGEN}