668 lines · 19.8 KB · C
RawDownload
/*** $Id: ldo.c,v 2.105 2012/06/08 15:14:04 roberto Exp $** Stack and Call structure of Lua** See Copyright Notice in lua.h*/  #include <setjmp.h>#include <stdlib.h>#include <string.h> #define ldo_c#define LUA_CORE #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"    /*** {======================================================** 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_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   /* 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 */    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) */      }      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;  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 */  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;      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;      luaD_checkstack(L, p->maxstacksize);      func = restorestack(L, funcr);      n = cast_int(L->top - func) - 1;  /* number of real arguments */      for (; n < p->numparams; n++)        setnilvalue(L->top++);  /* complete missing arguments */      base = (!p->is_vararg) ? func + 1 : adjust_varargs(L, p, n);      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;      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 = 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--;  luaC_checkGC(L);}  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);  /* finish 'lua_callk' */  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 */  incr_top(L);  luaD_throw(L, -1);  /* jump back to 'lua_resume' */}  /*** do the work for 'lua_resume' in protected mode*/static void resume (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;

Lines 1–500 of 668.