Lua TeamLua 5.3.0-rc017ba73111 years agoHistory
717 lines · 21.9 KB · C
RawDownload
/*** $Id: ldo.c,v 2.135 2014/11/11 17:13:39 roberto Exp $** Stack and Call structure of Lua** See Copyright Notice in lua.h*/ #define ldo_c#define LUA_CORE #include "lprefix.h"  #include <setjmp.h>#include <stdlib.h>#include <string.h> #include "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 "lundump.h"#include "lvm.h"#include "lzio.h"   #define errorstatus(s)	((s) > LUA_YIELD)  /*** {======================================================** 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)				/* { */ #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_POSIX)				/* }{ */ /* in POSIX, 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							/* }{ */ /* ISO C 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							/* } */   /* 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;}  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 */    global_State *g = G(L);    L->status = cast_byte(errcode);  /* mark it as dead */    if (g->mainthread->errorJmp) {  /* main thread has a handler? */      setobjs2s(L, g->mainthread->top++, L->top - 1);  /* copy error obj. */      luaD_throw(g->mainthread, errcode);  /* re-throw in main thread */    }    else {  /* no handler at all; abort */      if (g->panic) {  /* panic function? */        seterrorobj(L, errcode, L->top);  /* assume EXTRA_STACK */        if (L->ci->top < L->top)          L->ci->top = L->top;  /* pushing msg. can break this invariant */        lua_unlock(L);        g->panic(L);  /* call panic function (last chance to jump out) */      }      abort();    }  }}  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;  UpVal *up;  L->top = (L->top - oldstack) + L->stack;  for (up = L->openupval; up != NULL; up = up->u.open.next)    up->v = (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 (L->stacksize > LUAI_MAXSTACK)  /* was handling stack overflow? */    luaE_freeCI(L);  /* free all CIs (list grew because of an error) */  else    luaE_shrinkCI(L);  /* shrink list */  if (inuse > LUAI_MAXSTACK ||  /* still 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;}  /*** Check whether __call metafield of 'func' is a function. If so, put** it in stack below original 'func' so that 'luaD_precall' can call** it. Raise an error if __call metafield is not a function.*/static void tryfuncTM (lua_State *L, StkId func) {  const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);  StkId p;  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);  L->top++;  /* slot ensured by caller */  setobj2s(L, func, tm);  /* tag method is the new function to be called */}   #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);      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 */      luaD_checkstack(L, 1);  /* ensure space for metamethod */      func = restorestack(L, funcr);  /* previous call may change stack */      tryfuncTM(L, func);  /* try to get '__call' metamethod */      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 = 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 handing 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--;}  /*** Completes the execution of an interrupted C function, calling its** continuation function.*/static void finishCcall (lua_State *L, int status) {  CallInfo *ci = L->ci;  int n;  /* must have a continuation and must be able to call it */  lua_assert(ci->u.c.k != NULL && L->nny == 0);  /* error status can only happen in a protected call */  lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);  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'; CIST_YPCALL and 'errfunc' already     handled */  adjustresults(L, ci->nresults);  /* call continuation function */  lua_unlock(L);  n = (*ci->u.c.k)(L, status, ci->u.c.ctx);  lua_lock(L);  api_checknelems(L, n);  /* finish 'luaD_precall' */  luaD_poscall(L, L->top - n);}  /*** Executes "full continuation" (everything in the stack) of a** previously interrupted coroutine until the stack is empty (or another** interruption long-jumps out of the loop). If the coroutine is** recovering from an error, 'ud' points to the error status, which must** be passed to the first continuation function (otherwise the default** status is LUA_YIELD).*/static void unroll (lua_State *L, void *ud) {  if (ud != NULL)  /* error status? */    finishCcall(L, *(int *)ud);  /* finish 'lua_pcallk' callee */  while (L->ci != &L->base_ci) {  /* something in the stack */    if (!isLua(L->ci))  /* C function? */      finishCcall(L, LUA_YIELD);  /* complete its execution */    else {  /* Lua function */      luaV_finishOp(L);  /* finish interrupted instruction */      luaV_execute(L);  /* execute down to higher C 'boundary' */    }  }}  /*** Try to find a suspended protected call (a "recover point") for the** given thread.*/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 */}  /*** Recovers from an error in a coroutine. Finds a recover point (if** there is one) and completes the execution of the interrupted** 'luaD_pcall'. If there is no recover point, returns zero.*/static int recover (lua_State *L, int status) {  StkId oldtop;  CallInfo *ci = findpcall(L);  if (ci == NULL) return 0;  /* no recovery point */

Lines 1–500 of 717.