mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 11:19:32 +03:00
6a42939fcd
These were categorized as the following:
* Dead assignment 23
* Dead increment 4
* Dead initialization 6
* Dead nested assignment 18
Most of these are harmless, but since actual issues can hide among them,
we correct them.
That said, there were a few return values that were being ignored that
appeared to merit some correction:
* `destroy_callback()` in `cmd/zfs/zfs_main.c` ignored the error from
`destroy_batched()`. We handle it by returning -1 if there is an
error.
* `zfs_do_upgrade()` in `cmd/zfs/zfs_main.c` ignored the error from
`zfs_for_each()`. We handle it by doing a binary OR of the error
value from the subsequent `zfs_for_each()` call to the existing
value. This is how errors are mostly handled inside `zfs_for_each()`.
The error value here is passed to exit from the zfs command, so doing
a binary or on it is better than what we did previously.
* `get_zap_prop()` in `module/zfs/zcp_get.c` ignored the error from
`dsl_prop_get_ds()` when the property is not of type string. We
return an error when it does. There is a small concern that the
`zfs_get_temporary_prop()` call would handle things, but in the case
that it does not, we would be pushing an uninitialized numval onto
the lua stack. It is expected that `dsl_prop_get_ds()` will succeed
anytime that `zfs_get_temporary_prop()` does, so that not giving it a
chance to fix things is not a problem.
* `draid_merge_impl()` in `tests/zfs-tests/cmd/draid.c` used
`nvlist_add_nvlist()` twice in ways in which errors are expected to
be impossible, so we switch to `fnvlist_add_nvlist()`.
A few notable ones did not merit use of the return value, so we
suppressed it with `(void)`:
* `write_free_diffs()` in `lib/libzfs/libzfs_diff.c` ignored the error
value from `describe_free()`. A look through the commit history
revealed that this was intentional.
* `arc_evict_hdr()` in `module/zfs/arc.c` did not need to use the
returned handle from `arc_hdr_realloc()` because it is already
referenced in lists.
* `spa_vdev_detach()` in `module/zfs/spa.c` has a comment explicitly
saying not to use the error from `vdev_label_init()` because whatever
causes the error could be the reason why a detach is being done.
Unfortunately, I am not presently able to analyze the kernel modules
with Clang's static analyzer, so I could have missed some cases of this.
In cases where reports were present in code that is duplicated between
Linux and FreeBSD, I made a conscious effort to fix the FreeBSD version
too.
After this commit is merged, regressions like dee8934
should become
extremely obvious with Clang's static analyzer since a regression would
appear in the results as the only instance of unused code. That assumes
that Coverity does not catch the issue first.
My local branch with fixes from all of my outstanding non-draft pull
requests shows 118 reports from Clang's static anlayzer after this
patch. That is down by 51 from 169.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Cedric Berger <cedric@precidata.com>
Signed-off-by: Richard Yao <richard.yao@alumni.stonybrook.edu>
Closes #13986
759 lines
22 KiB
C
759 lines
22 KiB
C
/*
|
|
** $Id: ldo.c,v 2.108.1.3 2013/11/08 18:22:50 roberto Exp $
|
|
** Stack and Call structure of Lua
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
|
|
#define ldo_c
|
|
#define LUA_CORE
|
|
|
|
#include <sys/lua/lua.h>
|
|
|
|
#include "lapi.h"
|
|
#include "ldebug.h"
|
|
#include "ldo.h"
|
|
#include "lfunc.h"
|
|
#include "lgc.h"
|
|
#include "lmem.h"
|
|
#include "lobject.h"
|
|
#include "lopcodes.h"
|
|
#include "lparser.h"
|
|
#include "lstate.h"
|
|
#include "lstring.h"
|
|
#include "ltable.h"
|
|
#include "ltm.h"
|
|
#include "lvm.h"
|
|
#include "lzio.h"
|
|
|
|
|
|
|
|
/* Return the number of bytes available on the stack. */
|
|
#if defined (_KERNEL) && defined(__linux__)
|
|
#include <asm/current.h>
|
|
static intptr_t stack_remaining(void) {
|
|
intptr_t local;
|
|
local = (intptr_t)&local - (intptr_t)current->stack;
|
|
return local;
|
|
}
|
|
#elif defined (_KERNEL) && defined(__FreeBSD__)
|
|
#include <sys/pcpu.h>
|
|
static intptr_t stack_remaining(void) {
|
|
intptr_t local;
|
|
local = (intptr_t)&local - (intptr_t)curthread->td_kstack;
|
|
return local;
|
|
}
|
|
#else
|
|
static intptr_t stack_remaining(void) {
|
|
return INTPTR_MAX;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** {======================================================
|
|
** Error-recovery functions
|
|
** =======================================================
|
|
*/
|
|
|
|
/*
|
|
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
|
|
** default, Lua handles errors with exceptions when compiling as
|
|
** C++ code, with _longjmp/_setjmp when asked to use them, and with
|
|
** longjmp/setjmp otherwise.
|
|
*/
|
|
#if !defined(LUAI_THROW)
|
|
|
|
#ifdef _KERNEL
|
|
|
|
#ifdef __linux__
|
|
#if defined(__i386__)
|
|
#define JMP_BUF_CNT 6
|
|
#elif defined(__x86_64__)
|
|
#define JMP_BUF_CNT 8
|
|
#elif defined(__sparc__) && defined(__arch64__)
|
|
#define JMP_BUF_CNT 6
|
|
#elif defined(__powerpc__)
|
|
#define JMP_BUF_CNT 26
|
|
#elif defined(__aarch64__)
|
|
#define JMP_BUF_CNT 64
|
|
#elif defined(__arm__)
|
|
#define JMP_BUF_CNT 65
|
|
#elif defined(__mips__)
|
|
#define JMP_BUF_CNT 12
|
|
#elif defined(__s390x__)
|
|
#define JMP_BUF_CNT 18
|
|
#elif defined(__riscv)
|
|
#define JMP_BUF_CNT 64
|
|
#else
|
|
#define JMP_BUF_CNT 1
|
|
#endif
|
|
|
|
typedef struct _label_t { long long unsigned val[JMP_BUF_CNT]; } label_t;
|
|
|
|
int setjmp(label_t *) __attribute__ ((__nothrow__));
|
|
extern __attribute__((noreturn)) void longjmp(label_t *);
|
|
|
|
#define LUAI_THROW(L,c) longjmp(&(c)->b)
|
|
#define LUAI_TRY(L,c,a) if (setjmp(&(c)->b) == 0) { a }
|
|
#define luai_jmpbuf label_t
|
|
|
|
/* unsupported arches will build but not be able to run lua programs */
|
|
#if JMP_BUF_CNT == 1
|
|
int setjmp (label_t *buf) {
|
|
return 1;
|
|
}
|
|
|
|
void longjmp (label_t * buf) {
|
|
for (;;);
|
|
}
|
|
#endif
|
|
#else
|
|
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
#endif
|
|
|
|
#else /* _KERNEL */
|
|
|
|
#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP)
|
|
/* C++ exceptions */
|
|
#define LUAI_THROW(L,c) throw(c)
|
|
#define LUAI_TRY(L,c,a) \
|
|
try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
|
|
#define luai_jmpbuf int /* dummy variable */
|
|
|
|
#elif defined(LUA_USE_ULONGJMP)
|
|
/* in Unix, try _longjmp/_setjmp (more efficient) */
|
|
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
|
|
#else
|
|
/* default handling with long jumps */
|
|
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
|
|
#endif
|
|
|
|
#endif /* _KERNEL */
|
|
|
|
#endif /* LUAI_THROW */
|
|
|
|
|
|
/* chain list of long jump buffers */
|
|
struct lua_longjmp {
|
|
struct lua_longjmp *previous;
|
|
luai_jmpbuf b;
|
|
volatile int status; /* error code */
|
|
};
|
|
|
|
|
|
static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
|
switch (errcode) {
|
|
case LUA_ERRMEM: { /* memory error? */
|
|
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
|
|
break;
|
|
}
|
|
case LUA_ERRERR: {
|
|
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
|
break;
|
|
}
|
|
default: {
|
|
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
|
break;
|
|
}
|
|
}
|
|
L->top = oldtop + 1;
|
|
}
|
|
|
|
/*
|
|
* Silence infinite recursion warning which was added to -Wall in gcc 12.1
|
|
*/
|
|
#if defined(HAVE_INFINITE_RECURSION)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Winfinite-recursion"
|
|
#endif
|
|
|
|
l_noret luaD_throw (lua_State *L, int errcode) {
|
|
if (L->errorJmp) { /* thread has an error handler? */
|
|
L->errorJmp->status = errcode; /* set status */
|
|
LUAI_THROW(L, L->errorJmp); /* jump to it */
|
|
}
|
|
else { /* thread has no error handler */
|
|
L->status = cast_byte(errcode); /* mark it as dead */
|
|
if (G(L)->mainthread->errorJmp) { /* main thread has a handler? */
|
|
setobjs2s(L, G(L)->mainthread->top++, L->top - 1); /* copy error obj. */
|
|
luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */
|
|
}
|
|
else { /* no handler at all; abort */
|
|
if (G(L)->panic) { /* panic function? */
|
|
lua_unlock(L);
|
|
G(L)->panic(L); /* call it (last chance to jump out) */
|
|
}
|
|
panic("no error handler");
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_INFINITE_RECURSION)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
|
|
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
|
unsigned short oldnCcalls = L->nCcalls;
|
|
struct lua_longjmp lj;
|
|
lj.status = LUA_OK;
|
|
lj.previous = L->errorJmp; /* chain new error handler */
|
|
L->errorJmp = &lj;
|
|
LUAI_TRY(L, &lj,
|
|
(*f)(L, ud);
|
|
);
|
|
L->errorJmp = lj.previous; /* restore old error handler */
|
|
L->nCcalls = oldnCcalls;
|
|
return lj.status;
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
static void correctstack (lua_State *L, TValue *oldstack) {
|
|
CallInfo *ci;
|
|
GCObject *up;
|
|
L->top = (L->top - oldstack) + L->stack;
|
|
for (up = L->openupval; up != NULL; up = up->gch.next)
|
|
gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
|
ci->top = (ci->top - oldstack) + L->stack;
|
|
ci->func = (ci->func - oldstack) + L->stack;
|
|
if (isLua(ci))
|
|
ci->u.l.base = (ci->u.l.base - oldstack) + L->stack;
|
|
}
|
|
}
|
|
|
|
|
|
/* some space for error handling */
|
|
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
|
|
|
|
|
void luaD_reallocstack (lua_State *L, int newsize) {
|
|
TValue *oldstack = L->stack;
|
|
int lim = L->stacksize;
|
|
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
|
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
|
|
luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue);
|
|
for (; lim < newsize; lim++)
|
|
setnilvalue(L->stack + lim); /* erase new segment */
|
|
L->stacksize = newsize;
|
|
L->stack_last = L->stack + newsize - EXTRA_STACK;
|
|
correctstack(L, oldstack);
|
|
}
|
|
|
|
|
|
void luaD_growstack (lua_State *L, int n) {
|
|
int size = L->stacksize;
|
|
if (size > LUAI_MAXSTACK) /* error after extra size? */
|
|
luaD_throw(L, LUA_ERRERR);
|
|
else {
|
|
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
|
|
int newsize = 2 * size;
|
|
if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK;
|
|
if (newsize < needed) newsize = needed;
|
|
if (newsize > LUAI_MAXSTACK) { /* stack overflow? */
|
|
luaD_reallocstack(L, ERRORSTACKSIZE);
|
|
luaG_runerror(L, "stack overflow");
|
|
}
|
|
else
|
|
luaD_reallocstack(L, newsize);
|
|
}
|
|
}
|
|
|
|
|
|
static int stackinuse (lua_State *L) {
|
|
CallInfo *ci;
|
|
StkId lim = L->top;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
|
lua_assert(ci->top <= L->stack_last);
|
|
if (lim < ci->top) lim = ci->top;
|
|
}
|
|
return cast_int(lim - L->stack) + 1; /* part of stack in use */
|
|
}
|
|
|
|
|
|
void luaD_shrinkstack (lua_State *L) {
|
|
int inuse = stackinuse(L);
|
|
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
|
|
if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK;
|
|
if (inuse > LUAI_MAXSTACK || /* handling stack overflow? */
|
|
goodsize >= L->stacksize) /* would grow instead of shrink? */
|
|
condmovestack(L); /* don't change stack (change only for debugging) */
|
|
else
|
|
luaD_reallocstack(L, goodsize); /* shrink it */
|
|
}
|
|
|
|
|
|
void luaD_hook (lua_State *L, int event, int line) {
|
|
lua_Hook hook = L->hook;
|
|
if (hook && L->allowhook) {
|
|
CallInfo *ci = L->ci;
|
|
ptrdiff_t top = savestack(L, L->top);
|
|
ptrdiff_t ci_top = savestack(L, ci->top);
|
|
lua_Debug ar;
|
|
ar.event = event;
|
|
ar.currentline = line;
|
|
ar.i_ci = ci;
|
|
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
|
ci->top = L->top + LUA_MINSTACK;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
L->allowhook = 0; /* cannot call hooks inside a hook */
|
|
ci->callstatus |= CIST_HOOKED;
|
|
lua_unlock(L);
|
|
(*hook)(L, &ar);
|
|
lua_lock(L);
|
|
lua_assert(!L->allowhook);
|
|
L->allowhook = 1;
|
|
ci->top = restorestack(L, ci_top);
|
|
L->top = restorestack(L, top);
|
|
ci->callstatus &= ~CIST_HOOKED;
|
|
}
|
|
}
|
|
|
|
|
|
static void callhook (lua_State *L, CallInfo *ci) {
|
|
int hook = LUA_HOOKCALL;
|
|
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
|
if (isLua(ci->previous) &&
|
|
GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) {
|
|
ci->callstatus |= CIST_TAIL;
|
|
hook = LUA_HOOKTAILCALL;
|
|
}
|
|
luaD_hook(L, hook, -1);
|
|
ci->u.l.savedpc--; /* correct 'pc' */
|
|
}
|
|
|
|
|
|
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
|
|
int i;
|
|
int nfixargs = p->numparams;
|
|
StkId base, fixed;
|
|
lua_assert(actual >= nfixargs);
|
|
/* move fixed parameters to final position */
|
|
luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */
|
|
fixed = L->top - actual; /* first fixed argument */
|
|
base = L->top; /* final position of first argument */
|
|
for (i=0; i<nfixargs; i++) {
|
|
setobjs2s(L, L->top++, fixed + i);
|
|
setnilvalue(fixed + i);
|
|
}
|
|
return base;
|
|
}
|
|
|
|
|
|
static StkId tryfuncTM (lua_State *L, StkId func) {
|
|
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
|
|
StkId p;
|
|
ptrdiff_t funcr = savestack(L, func);
|
|
if (!ttisfunction(tm))
|
|
luaG_typeerror(L, func, "call");
|
|
/* Open a hole inside the stack at `func' */
|
|
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
|
|
incr_top(L);
|
|
func = restorestack(L, funcr); /* previous call may change stack */
|
|
setobj2s(L, func, tm); /* tag method is the new function to be called */
|
|
return func;
|
|
}
|
|
|
|
|
|
|
|
#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
|
|
|
|
|
|
/*
|
|
** returns true if function has been executed (C function)
|
|
*/
|
|
int luaD_precall (lua_State *L, StkId func, int nresults) {
|
|
lua_CFunction f;
|
|
CallInfo *ci;
|
|
int n; /* number of arguments (Lua) or returns (C) */
|
|
ptrdiff_t funcr = savestack(L, func);
|
|
switch (ttype(func)) {
|
|
case LUA_TLCF: /* light C function */
|
|
f = fvalue(func);
|
|
goto Cfunc;
|
|
case LUA_TCCL: { /* C closure */
|
|
f = clCvalue(func)->f;
|
|
Cfunc:
|
|
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
|
ci = next_ci(L); /* now 'enter' new function */
|
|
ci->nresults = nresults;
|
|
ci->func = restorestack(L, funcr);
|
|
ci->top = L->top + LUA_MINSTACK;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
ci->callstatus = 0;
|
|
luaC_checkGC(L); /* stack grow uses memory */
|
|
if (L->hookmask & LUA_MASKCALL)
|
|
luaD_hook(L, LUA_HOOKCALL, -1);
|
|
lua_unlock(L);
|
|
n = (*f)(L); /* do the actual call */
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
luaD_poscall(L, L->top - n);
|
|
return 1;
|
|
}
|
|
case LUA_TLCL: { /* Lua function: prepare its call */
|
|
StkId base;
|
|
Proto *p = clLvalue(func)->p;
|
|
n = cast_int(L->top - func) - 1; /* number of real arguments */
|
|
luaD_checkstack(L, p->maxstacksize + p->numparams);
|
|
for (; n < p->numparams; n++)
|
|
setnilvalue(L->top++); /* complete missing arguments */
|
|
if (!p->is_vararg) {
|
|
func = restorestack(L, funcr);
|
|
base = func + 1;
|
|
}
|
|
else {
|
|
base = adjust_varargs(L, p, n);
|
|
func = restorestack(L, funcr); /* previous call can change stack */
|
|
}
|
|
ci = next_ci(L); /* now 'enter' new function */
|
|
ci->nresults = nresults;
|
|
ci->func = func;
|
|
ci->u.l.base = base;
|
|
ci->top = base + p->maxstacksize;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
ci->u.l.savedpc = p->code; /* starting point */
|
|
ci->callstatus = CIST_LUA;
|
|
L->top = ci->top;
|
|
luaC_checkGC(L); /* stack grow uses memory */
|
|
if (L->hookmask & LUA_MASKCALL)
|
|
callhook(L, ci);
|
|
return 0;
|
|
}
|
|
default: { /* not a function */
|
|
func = tryfuncTM(L, func); /* retry with 'function' tag method */
|
|
return luaD_precall(L, func, nresults); /* now it must be a function */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int luaD_poscall (lua_State *L, StkId firstResult) {
|
|
StkId res;
|
|
int wanted, i;
|
|
CallInfo *ci = L->ci;
|
|
if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) {
|
|
if (L->hookmask & LUA_MASKRET) {
|
|
ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */
|
|
luaD_hook(L, LUA_HOOKRET, -1);
|
|
firstResult = restorestack(L, fr);
|
|
}
|
|
L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */
|
|
}
|
|
res = ci->func; /* res == final position of 1st result */
|
|
wanted = ci->nresults;
|
|
L->ci = ci->previous; /* back to caller */
|
|
/* move results to correct place */
|
|
for (i = wanted; i != 0 && firstResult < L->top; i--)
|
|
setobjs2s(L, res++, firstResult++);
|
|
while (i-- > 0)
|
|
setnilvalue(res++);
|
|
L->top = res;
|
|
return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
|
|
}
|
|
|
|
|
|
/*
|
|
** Call a function (C or Lua). The function to be called is at *func.
|
|
** The arguments are on the stack, right after the function.
|
|
** When returns, all the results are on the stack, starting at the original
|
|
** function position.
|
|
*/
|
|
void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) {
|
|
if (++L->nCcalls >= LUAI_MAXCCALLS) {
|
|
if (L->nCcalls == LUAI_MAXCCALLS)
|
|
luaG_runerror(L, "C stack overflow");
|
|
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
|
|
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
|
}
|
|
intptr_t remaining = stack_remaining();
|
|
if (L->runerror == 0 && remaining < LUAI_MINCSTACK)
|
|
luaG_runerror(L, "C stack overflow");
|
|
if (L->runerror != 0 && remaining < LUAI_MINCSTACK / 2)
|
|
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
|
if (!allowyield) L->nny++;
|
|
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
|
luaV_execute(L); /* call it */
|
|
if (!allowyield) L->nny--;
|
|
L->nCcalls--;
|
|
}
|
|
|
|
|
|
static void finishCcall (lua_State *L) {
|
|
CallInfo *ci = L->ci;
|
|
int n;
|
|
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
|
|
lua_assert(L->nny == 0);
|
|
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
|
|
ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */
|
|
L->errfunc = ci->u.c.old_errfunc;
|
|
}
|
|
/* finish 'lua_callk'/'lua_pcall' */
|
|
adjustresults(L, ci->nresults);
|
|
/* call continuation function */
|
|
if (!(ci->callstatus & CIST_STAT)) /* no call status? */
|
|
ci->u.c.status = LUA_YIELD; /* 'default' status */
|
|
lua_assert(ci->u.c.status != LUA_OK);
|
|
ci->callstatus = (ci->callstatus & ~(CIST_YPCALL | CIST_STAT)) | CIST_YIELDED;
|
|
lua_unlock(L);
|
|
n = (*ci->u.c.k)(L);
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
/* finish 'luaD_precall' */
|
|
luaD_poscall(L, L->top - n);
|
|
}
|
|
|
|
|
|
static void unroll (lua_State *L, void *ud) {
|
|
UNUSED(ud);
|
|
for (;;) {
|
|
if (L->ci == &L->base_ci) /* stack is empty? */
|
|
return; /* coroutine finished normally */
|
|
if (!isLua(L->ci)) /* C function? */
|
|
finishCcall(L);
|
|
else { /* Lua function */
|
|
luaV_finishOp(L); /* finish interrupted instruction */
|
|
luaV_execute(L); /* execute down to higher C 'boundary' */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** check whether thread has a suspended protected call
|
|
*/
|
|
static CallInfo *findpcall (lua_State *L) {
|
|
CallInfo *ci;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */
|
|
if (ci->callstatus & CIST_YPCALL)
|
|
return ci;
|
|
}
|
|
return NULL; /* no pending pcall */
|
|
}
|
|
|
|
|
|
static int recover (lua_State *L, int status) {
|
|
StkId oldtop;
|
|
CallInfo *ci = findpcall(L);
|
|
if (ci == NULL) return 0; /* no recovery point */
|
|
/* "finish" luaD_pcall */
|
|
oldtop = restorestack(L, ci->extra);
|
|
luaF_close(L, oldtop);
|
|
seterrorobj(L, status, oldtop);
|
|
L->ci = ci;
|
|
L->allowhook = ci->u.c.old_allowhook;
|
|
L->nny = 0; /* should be zero to be yieldable */
|
|
luaD_shrinkstack(L);
|
|
L->errfunc = ci->u.c.old_errfunc;
|
|
ci->callstatus |= CIST_STAT; /* call has error status */
|
|
ci->u.c.status = status; /* (here it is) */
|
|
return 1; /* continue running the coroutine */
|
|
}
|
|
|
|
|
|
/*
|
|
** signal an error in the call to 'resume', not in the execution of the
|
|
** coroutine itself. (Such errors should not be handled by any coroutine
|
|
** error handler and should not kill the coroutine.)
|
|
*/
|
|
static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) {
|
|
L->top = firstArg; /* remove args from the stack */
|
|
setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */
|
|
api_incr_top(L);
|
|
luaD_throw(L, -1); /* jump back to 'lua_resume' */
|
|
}
|
|
|
|
|
|
/*
|
|
** do the work for 'lua_resume' in protected mode
|
|
*/
|
|
static void resume_cb (lua_State *L, void *ud) {
|
|
int nCcalls = L->nCcalls;
|
|
StkId firstArg = cast(StkId, ud);
|
|
CallInfo *ci = L->ci;
|
|
if (nCcalls >= LUAI_MAXCCALLS)
|
|
resume_error(L, "C stack overflow", firstArg);
|
|
if (L->status == LUA_OK) { /* may be starting a coroutine */
|
|
if (ci != &L->base_ci) /* not in base level? */
|
|
resume_error(L, "cannot resume non-suspended coroutine", firstArg);
|
|
/* coroutine is in base level; start running it */
|
|
if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */
|
|
luaV_execute(L); /* call it */
|
|
}
|
|
else if (L->status != LUA_YIELD)
|
|
resume_error(L, "cannot resume dead coroutine", firstArg);
|
|
else { /* resuming from previous yield */
|
|
L->status = LUA_OK;
|
|
ci->func = restorestack(L, ci->extra);
|
|
if (isLua(ci)) /* yielded inside a hook? */
|
|
luaV_execute(L); /* just continue running Lua code */
|
|
else { /* 'common' yield */
|
|
if (ci->u.c.k != NULL) { /* does it have a continuation? */
|
|
int n;
|
|
ci->u.c.status = LUA_YIELD; /* 'default' status */
|
|
ci->callstatus |= CIST_YIELDED;
|
|
lua_unlock(L);
|
|
n = (*ci->u.c.k)(L); /* call continuation */
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
firstArg = L->top - n; /* yield results come from continuation */
|
|
}
|
|
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
|
|
}
|
|
unroll(L, NULL);
|
|
}
|
|
lua_assert(nCcalls == L->nCcalls);
|
|
}
|
|
|
|
|
|
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) {
|
|
int status;
|
|
int oldnny = L->nny; /* save 'nny' */
|
|
lua_lock(L);
|
|
luai_userstateresume(L, nargs);
|
|
L->nCcalls = (from) ? from->nCcalls + 1 : 1;
|
|
L->nny = 0; /* allow yields */
|
|
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
|
status = luaD_rawrunprotected(L, resume_cb, L->top - nargs);
|
|
if (status == -1) /* error calling 'lua_resume'? */
|
|
status = LUA_ERRRUN;
|
|
else { /* yield or regular error */
|
|
while (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
|
if (recover(L, status)) /* recover point? */
|
|
status = luaD_rawrunprotected(L, unroll, NULL); /* run continuation */
|
|
else { /* unrecoverable error */
|
|
L->status = cast_byte(status); /* mark thread as `dead' */
|
|
seterrorobj(L, status, L->top);
|
|
L->ci->top = L->top;
|
|
break;
|
|
}
|
|
}
|
|
lua_assert(status == L->status);
|
|
}
|
|
L->nny = oldnny; /* restore 'nny' */
|
|
L->nCcalls--;
|
|
lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0));
|
|
lua_unlock(L);
|
|
return status;
|
|
}
|
|
|
|
|
|
LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
|
|
CallInfo *ci = L->ci;
|
|
luai_userstateyield(L, nresults);
|
|
lua_lock(L);
|
|
api_checknelems(L, nresults);
|
|
if (L->nny > 0) {
|
|
if (L != G(L)->mainthread)
|
|
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
|
else
|
|
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
|
}
|
|
L->status = LUA_YIELD;
|
|
ci->extra = savestack(L, ci->func); /* save current 'func' */
|
|
if (isLua(ci)) { /* inside a hook? */
|
|
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
|
}
|
|
else {
|
|
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
|
ci->u.c.ctx = ctx; /* save context */
|
|
ci->func = L->top - nresults - 1; /* protect stack below results */
|
|
luaD_throw(L, LUA_YIELD);
|
|
}
|
|
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
|
lua_unlock(L);
|
|
return 0; /* return to 'luaD_hook' */
|
|
}
|
|
|
|
|
|
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
|
ptrdiff_t old_top, ptrdiff_t ef) {
|
|
int status;
|
|
CallInfo *old_ci = L->ci;
|
|
lu_byte old_allowhooks = L->allowhook;
|
|
unsigned short old_nny = L->nny;
|
|
ptrdiff_t old_errfunc = L->errfunc;
|
|
L->errfunc = ef;
|
|
status = luaD_rawrunprotected(L, func, u);
|
|
if (status != LUA_OK) { /* an error occurred? */
|
|
StkId oldtop = restorestack(L, old_top);
|
|
luaF_close(L, oldtop); /* close possible pending closures */
|
|
seterrorobj(L, status, oldtop);
|
|
L->ci = old_ci;
|
|
L->allowhook = old_allowhooks;
|
|
L->nny = old_nny;
|
|
luaD_shrinkstack(L);
|
|
}
|
|
L->errfunc = old_errfunc;
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** Execute a protected parser.
|
|
*/
|
|
struct SParser { /* data to `f_parser' */
|
|
ZIO *z;
|
|
Mbuffer buff; /* dynamic structure used by the scanner */
|
|
Dyndata dyd; /* dynamic structures used by the parser */
|
|
const char *mode;
|
|
const char *name;
|
|
};
|
|
|
|
|
|
static void checkmode (lua_State *L, const char *mode, const char *x) {
|
|
if (mode && strchr(mode, x[0]) == NULL) {
|
|
luaO_pushfstring(L,
|
|
"attempt to load a %s chunk (mode is " LUA_QS ")", x, mode);
|
|
luaD_throw(L, LUA_ERRSYNTAX);
|
|
}
|
|
}
|
|
|
|
|
|
static void f_parser (lua_State *L, void *ud) {
|
|
int i;
|
|
Closure *cl;
|
|
struct SParser *p = cast(struct SParser *, ud);
|
|
int c = zgetc(p->z); /* read first character */
|
|
lua_assert(c != LUA_SIGNATURE[0]); /* binary not supported */
|
|
checkmode(L, p->mode, "text");
|
|
cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
|
|
lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
|
|
for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */
|
|
UpVal *up = luaF_newupval(L);
|
|
cl->l.upvals[i] = up;
|
|
luaC_objbarrier(L, cl, up);
|
|
}
|
|
}
|
|
|
|
|
|
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
|
const char *mode) {
|
|
struct SParser p;
|
|
int status;
|
|
L->nny++; /* cannot yield during parsing */
|
|
p.z = z; p.name = name; p.mode = mode;
|
|
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
|
|
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
|
|
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
|
|
luaZ_initbuffer(L, &p.buff);
|
|
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
|
|
luaZ_freebuffer(L, &p.buff);
|
|
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
|
|
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
|
|
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
|
|
L->nny--;
|
|
return status;
|
|
}
|