/* ** $Id: lapi.c $ ** Lua API ** See Copyright Notice in lua.h */ #define lapi_c #define LUA_CORE #include "lprefix.h" #include #include #include #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 "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" const char lua_ident[] = "$LuaVersion: " LUA_COPYRIGHT " $" "$LuaAuthors: " LUA_AUTHORS " $"; /* ** Test for a valid index. ** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. ** However, it covers the most common cases in a faster way. */ #define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) /* test for pseudo index */ #define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) /* test for upvalue */ #define isupvalue(i) ((i) < LUA_REGISTRYINDEX) static TValue *index2value (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { StkId o = ci->func + idx; api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); if (o >= L->top) return &G(L)->nilvalue; else return s2v(o); } else if (!ispseudo(idx)) { /* negative index */ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); return s2v(L->top + idx); } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); if (ttislcf(s2v(ci->func))) /* light C function? */ return &G(L)->nilvalue; /* it has no upvalues */ else { CClosure *func = clCvalue(s2v(ci->func)); return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue; } } } static StkId index2stack (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { StkId o = ci->func + idx; api_check(L, o < L->top, "unacceptable index"); return o; } else { /* non-positive index */ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); api_check(L, !ispseudo(idx), "invalid index"); return L->top + idx; } } LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci = L->ci; lua_lock(L); api_check(L, n >= 0, "negative 'n'"); if (L->stack_last - L->top > n) /* stack large enough? */ res = 1; /* yes; check is OK */ else { /* no; need to grow stack */ int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ res = luaD_growstack(L, n, 0); } if (res && ci->top < L->top + n) ci->top = L->top + n; /* adjust frame top */ lua_unlock(L); return res; } LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, to->ci->top - to->top >= n, "stack overflow"); from->top -= n; for (i = 0; i < n; i++) { setobjs2s(to, to->top, from->top + i); to->top++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { lua_CFunction old; lua_lock(L); old = G(L)->panic; G(L)->panic = panicf; lua_unlock(L); return old; } LUA_API lua_Number lua_version (lua_State *L) { UNUSED(L); return LUA_VERSION_NUM; } /* ** basic stack manipulation */ /* ** convert an acceptable stack index into an absolute index */ LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx : cast_int(L->top - L->ci->func) + idx; } LUA_API int lua_gettop (lua_State *L) { return cast_int(L->top - (L->ci->func + 1)); } LUA_API void lua_settop (lua_State *L, int idx) { CallInfo *ci = L->ci; StkId func = ci->func; ptrdiff_t diff; /* difference for new top */ lua_lock(L); if (idx >= 0) { api_check(L, idx <= ci->top - (func + 1), "new top too large"); diff = ((func + 1) + idx) - L->top; for (; diff > 0; diff--) setnilvalue(s2v(L->top++)); /* clear new slots */ } else { api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } if (diff < 0 && hastocloseCfunc(ci->nresults)) luaF_close(L, L->top + diff, LUA_OK); L->top += diff; /* correct top only after closing any upvalue */ lua_unlock(L); } /* ** Reverse the stack segment from 'from' to 'to' ** (auxiliary to 'lua_rotate') ** Note that we move(copy) only the value inside the stack. ** (We do not move additional fields that may exist.) */ static void reverse (lua_State *L, StkId from, StkId to) { for (; from < to; from++, to--) { TValue temp; setobj(L, &temp, s2v(from)); setobjs2s(L, from, to); setobj2s(L, to, &temp); } } /* ** Let x = AB, where A is a prefix of length 'n'. Then, ** rotate x n == BA. But BA == (A^r . B^r)^r. */ LUA_API void lua_rotate (lua_State *L, int idx, int n) { StkId p, t, m; lua_lock(L); t = L->top - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ reverse(L, p, m); /* reverse the prefix with length 'n' */ reverse(L, m + 1, t); /* reverse the suffix */ reverse(L, p, t); /* reverse the entire segment */ lua_unlock(L); } LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { TValue *fr, *to; lua_lock(L); fr = index2value(L, fromidx); to = index2value(L, toidx); api_check(l, isvalid(L, to), "invalid index"); setobj(L, to, fr); if (isupvalue(toidx)) /* function upvalue? */ luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ lua_unlock(L); } LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); setobj2s(L, L->top, index2value(L, idx)); api_incr_top(L); lua_unlock(L); } /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (isvalid(L, o) ? ttype(o) : LUA_TNONE); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag"); return ttypename(t); } LUA_API int lua_iscfunction (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttislcf(o) || (ttisCclosure(o))); } LUA_API int lua_isinteger (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return ttisinteger(o); } LUA_API int lua_isnumber (lua_State *L, int idx) { lua_Number n; const TValue *o = index2value(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttisstring(o) || cvt2str(o)); } LUA_API int lua_isuserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttisfulluserdata(o) || ttislightuserdata(o)); } LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { const TValue *o1 = index2value(L, index1); const TValue *o2 = index2value(L, index2); return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; } LUA_API void lua_arith (lua_State *L, int op) { lua_lock(L); if (op != LUA_OPUNM && op != LUA_OPBNOT) api_checknelems(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); setobjs2s(L, L->top, L->top - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); L->top--; /* remove second operand */ lua_unlock(L); } LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { const TValue *o1; const TValue *o2; int i = 0; lua_lock(L); /* may call tag method */ o1 = index2value(L, index1); o2 = index2value(L, index2); if (isvalid(L, o1) && isvalid(L, o2)) { ptrdiff_t top = savestack(L, L->top); switch (op) { case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; default: api_check(L, 0, "invalid option"); } L->top = restorestack(L, top); } lua_unlock(L); return i; } LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { size_t sz = luaO_str2num(s, s2v(L->top)); if (sz != 0) api_incr_top(L); return sz; } LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { lua_Number n; const TValue *o = index2value(L, idx); int isnum = tonumber(o, &n); if (!isnum) n = 0; /* call to 'tonumber' may change 'n' even if it fails */ if (pisnum) *pisnum = isnum; return n; } LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { lua_Integer res; const TValue *o = index2value(L, idx); int isnum = tointeger(o, &res); if (!isnum) res = 0; /* call to 'tointeger' may change 'n' even if it fails */ if (pisnum) *pisnum = isnum; return res; } LUA_API int lua_toboolean (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return !l_isfalse(o); } LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { TValue *o = index2value(L, idx); if (!ttisstring(o)) { if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; return NULL; } lua_lock(L); /* 'luaO_tostring' may create a new string */ luaO_tostring(L, o); luaC_checkGC(L); o = index2value(L, idx); /* previous call may reallocate the stack */ lua_unlock(L); } if (len != NULL) *len = vslen(o); return svalue(o); } LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_TSHRSTR: return tsvalue(o)->shrlen; case LUA_TLNGSTR: return tsvalue(o)->u.lnglen; case LUA_TUSERDATA: return uvalue(o)->len; case LUA_TTABLE: return luaH_getn(hvalue(o)); default: return 0; } } LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { const TValue *o = index2value(L, idx); if (ttislcf(o)) return fvalue(o); else if (ttisCclosure(o)) return clCvalue(o)->f; else return NULL; /* not a C function */ } static void *touserdata (const TValue *o) { switch (ttype(o)) { case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } LUA_API void *lua_touserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return touserdata(o); } LUA_API lua_State *lua_tothread (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } /* ** Returns a pointer to the internal representation of an object. ** Note that ANSI C does not allow the conversion of a pointer to ** function to a 'void*', so the conversion here goes through ** a 'size_t'. (As the returned pointer is only informative, this ** conversion should not be a problem.) */ LUA_API const void *lua_topointer (lua_State *L, int idx) { const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o))); case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: return touserdata(o); default: { if (iscollectable(o)) return gcvalue(o); else return NULL; } } } /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); setnilvalue(s2v(L->top)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); setfltvalue(s2v(L->top), n); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); setivalue(s2v(L->top), n); api_incr_top(L); lua_unlock(L); } /* ** Pushes on the stack a string with given length. Avoid using 's' when ** 'len' == 0 (as 's' can be NULL in that case), due to later use of ** 'memcmp' and 'memcpy'. */ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getstr(ts); } LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) setnilvalue(s2v(L->top)); else { TString *ts; ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return s; } LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); ret = luaO_pushvfstring(L, fmt, argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { setfvalue(s2v(L->top), fn); api_incr_top(L); } else { CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; L->top -= n; while (n--) { setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); /* does not need barrier because closure is white */ } setclCvalue(L, s2v(L->top), cl); api_incr_top(L); luaC_checkGC(L); } lua_unlock(L); } LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); setbvalue(s2v(L->top), (b != 0)); /* ensure that true is 1 */ api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); setpvalue(s2v(L->top), p); api_incr_top(L); lua_unlock(L); } LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); setthvalue(L, s2v(L->top), L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); } /* ** get functions (Lua -> stack) */ static int auxgetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { setobj2s(L, L->top, slot); api_incr_top(L); } else { setsvalue2s(L, L->top, str); api_incr_top(L); luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); } lua_unlock(L); return ttype(s2v(L->top - 1)); } LUA_API int lua_getglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); lua_lock(L); return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } LUA_API int lua_gettable (lua_State *L, int idx) { const TValue *slot; TValue *t; lua_lock(L); t = index2value(L, idx); if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { setobj2s(L, L->top - 1, slot); } else luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); lua_unlock(L); return ttype(s2v(L->top - 1)); } LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { lua_lock(L); return auxgetstr(L, index2value(L, idx), k); } LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { TValue *t; const TValue *slot; lua_lock(L); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { setobj2s(L, L->top, slot); } else { TValue aux; setivalue(&aux, n); luaV_finishget(L, t, &aux, L->top, slot); } api_incr_top(L); lua_unlock(L); return ttype(s2v(L->top - 1)); } static int finishrawget (lua_State *L, const TValue *val) { if (isempty(val)) /* avoid copying empty items to the stack */ setnilvalue(s2v(L->top)); else setobj2s(L, L->top, val); api_incr_top(L); lua_unlock(L); return ttype(s2v(L->top - 1)); } static Table *gettable (lua_State *L, int idx) { TValue *t = index2value(L, idx); api_check(L, ttistable(t), "table expected"); return hvalue(t); } LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; const TValue *val; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); val = luaH_get(t, s2v(L->top - 1)); L->top--; /* remove key */ return finishrawget(L, val); } LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); t = gettable(L, idx); return finishrawget(L, luaH_getint(t, n)); } LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { Table *t; TValue k; lua_lock(L); t = gettable(L, idx); setpvalue(&k, cast_voidp(p)); return finishrawget(L, luaH_get(t, &k)); } LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); t = luaH_new(L); sethvalue2s(L, L->top, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); luaC_checkGC(L); lua_unlock(L); } LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; int res = 0; lua_lock(L); obj = index2value(L, objindex); switch (ttype(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; case LUA_TUSERDATA: mt = uvalue(obj)->metatable; break; default: mt = G(L)->mt[ttype(obj)]; break; } if (mt != NULL) { sethvalue2s(L, L->top, mt); api_incr_top(L); res = 1; } lua_unlock(L); return res; } LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { TValue *o; int t; lua_lock(L); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (n <= 0 || n > uvalue(o)->nuvalue) { setnilvalue(s2v(L->top)); t = LUA_TNONE; } else { setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); t = ttype(s2v(L->top)); } api_incr_top(L); lua_unlock(L); return t; } /* ** set functions (stack -> Lua) */ /* ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); api_checknelems(L, 1); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); L->top--; /* pop value */ } else { setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ } LUA_API void lua_setglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); lua_lock(L); /* unlock done in 'auxsetstr' */ auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } LUA_API void lua_settable (lua_State *L, int idx) { TValue *t; const TValue *slot; lua_lock(L); api_checknelems(L, 2); t = index2value(L, idx); if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); } else luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); L->top -= 2; /* pop index and value */ lua_unlock(L); } LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { lua_lock(L); /* unlock done in 'auxsetstr' */ auxsetstr(L, index2value(L, idx), k); } LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { TValue *t; const TValue *slot; lua_lock(L); api_checknelems(L, 1); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); } else { TValue aux; setivalue(&aux, n); luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); } L->top--; /* pop value */ lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { Table *t; TValue *slot; lua_lock(L); api_checknelems(L, 2); t = gettable(L, idx); slot = luaH_set(L, t, s2v(L->top - 2)); setobj2t(L, slot, s2v(L->top - 1)); invalidateTMcache(t); luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); L->top -= 2; lua_unlock(L); } LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); luaH_setint(L, t, n, s2v(L->top - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); L->top--; lua_unlock(L); } LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { Table *t; TValue k, *slot; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); setpvalue(&k, cast_voidp(p)); slot = luaH_set(L, t, &k); setobj2t(L, slot, s2v(L->top - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); L->top--; lua_unlock(L); } LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); api_checknelems(L, 1); obj = index2value(L, objindex); if (ttisnil(s2v(L->top - 1))) mt = NULL; else { api_check(L, ttistable(s2v(L->top - 1)), "table expected"); mt = hvalue(s2v(L->top - 1)); } switch (ttype(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, gcvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, uvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } default: { G(L)->mt[ttype(obj)] = mt; break; } } L->top--; lua_unlock(L); return 1; } LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; lua_lock(L); api_checknelems(L, 1); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ else { setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); res = 1; } L->top--; lua_unlock(L); return res; } /* ** 'load' and 'call' functions (run Lua code) */ #define checkresults(L,na,nr) \ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ "results from function overflow current stack size") LUA_API void lua_callk (lua_State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k) { StkId func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); func = L->top - (nargs+1); if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } /* ** Execute a protected call. */ struct CallS { /* data to 'f_call' */ StkId func; int nresults; }; static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); luaD_callnoyield(L, c->func, c->nresults); } LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2stack(L, errfunc); api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); } else { /* prepare continuation (call is already protected by 'resume') */ CallInfo *ci = L->ci; ci->u.c.k = k; /* save continuation */ ci->u.c.ctx = ctx; /* save context */ /* save information for error recovery */ ci->u2.funcidx = cast_int(savestack(L, c.func)); ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ } adjustresults(L, nresults); lua_unlock(L); return status; } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode) { ZIO z; int status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ Table *reg = hvalue(&G(L)->l_registry); const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ setobj(L, f->upvals[0]->v, gt); luaC_barrier(L, f->upvals[0], gt); } } lua_unlock(L); return status; } LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); o = s2v(L->top - 1); if (isLfunction(o)) status = luaU_dump(L, getproto(o), writer, data, strip); else status = 1; lua_unlock(L); return status; } LUA_API int lua_status (lua_State *L) { return L->status; } /* ** Garbage-collection function */ LUA_API int lua_gc (lua_State *L, int what, ...) { va_list argp; int res = 0; global_State *g = G(L); lua_lock(L); va_start(argp, what); switch (what) { case LUA_GCSTOP: { g->gcrunning = 0; break; } case LUA_GCRESTART: { luaE_setdebt(g, 0); g->gcrunning = 1; break; } case LUA_GCCOLLECT: { luaC_fullgc(L, 0); break; } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ res = cast_int(gettotalbytes(g) >> 10); break; } case LUA_GCCOUNTB: { res = cast_int(gettotalbytes(g) & 0x3ff); break; } case LUA_GCSTEP: { int data = va_arg(argp, int); l_mem debt = 1; /* =1 to signal that it did an actual step */ lu_byte oldrunning = g->gcrunning; g->gcrunning = 1; /* allow GC to run */ if (data == 0) { luaE_setdebt(g, 0); /* do a basic step */ luaC_step(L); } else { /* add 'data' to total debt */ debt = cast(l_mem, data) * 1024 + g->GCdebt; luaE_setdebt(g, debt); luaC_checkGC(L); } g->gcrunning = oldrunning; /* restore previous state */ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ break; } case LUA_GCSETPAUSE: { int data = va_arg(argp, int); res = getgcparam(g->gcpause); setgcparam(g->gcpause, data); break; } case LUA_GCSETSTEPMUL: { int data = va_arg(argp, int); res = getgcparam(g->gcstepmul); setgcparam(g->gcstepmul, data); break; } case LUA_GCISRUNNING: { res = g->gcrunning; break; } case LUA_GCGEN: { int minormul = va_arg(argp, int); int majormul = va_arg(argp, int); res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; if (minormul != 0) g->genminormul = minormul; if (majormul != 0) setgcparam(g->genmajormul, majormul); luaC_changemode(L, KGC_GEN); break; } case LUA_GCINC: { int pause = va_arg(argp, int); int stepmul = va_arg(argp, int); int stepsize = va_arg(argp, int); res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; if (pause != 0) setgcparam(g->gcpause, pause); if (stepmul != 0) setgcparam(g->gcstepmul, stepmul); if (stepsize != 0) g->gcstepsize = stepsize; luaC_changemode(L, KGC_INC); break; } default: res = -1; /* invalid option */ } va_end(argp); lua_unlock(L); return res; } /* ** miscellaneous functions */ LUA_API int lua_error (lua_State *L) { lua_lock(L); api_checknelems(L, 1); luaG_errormsg(L); /* code unreachable; will unlock when control actually leaves the kernel */ return 0; /* to avoid warnings */ } LUA_API int lua_next (lua_State *L, int idx) { Table *t; int more; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); more = luaH_next(L, t, L->top - 1); if (more) { api_incr_top(L); } else /* no more elements */ L->top -= 1; /* remove key */ lua_unlock(L); return more; } LUA_API void lua_toclose (lua_State *L, int idx) { int nresults; StkId o; lua_lock(L); o = index2stack(L, idx); nresults = L->ci->nresults; api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o, "marked index below or equal new one"); luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ if (!hastocloseCfunc(nresults)) /* function not marked yet? */ L->ci->nresults = codeNresults(nresults); /* mark it */ lua_assert(hastocloseCfunc(L->ci->nresults)); lua_unlock(L); } LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); if (n >= 2) { luaV_concat(L, n); } else if (n == 0) { /* push empty string */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); api_incr_top(L); } /* else n == 1; nothing to do */ luaC_checkGC(L); lua_unlock(L); } LUA_API void lua_len (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); luaV_objlen(L, L->top, t); api_incr_top(L); lua_unlock(L); } LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { lua_Alloc f; lua_lock(L); if (ud) *ud = G(L)->ud; f = G(L)->frealloc; lua_unlock(L); return f; } LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { lua_lock(L); G(L)->ud = ud; G(L)->frealloc = f; lua_unlock(L); } void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { lua_lock(L); G(L)->ud_warn = ud; G(L)->warnf = f; lua_unlock(L); } void lua_warning (lua_State *L, const char *msg, int tocont) { lua_lock(L); luaE_warning(L, msg, tocont); lua_unlock(L); } LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { Udata *u; lua_lock(L); api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); u = luaS_newudata(L, size, nuvalue); setuvalue(L, s2v(L->top), u); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getudatamem(u); } static const char *aux_upvalue (TValue *fi, int n, TValue **val, GCObject **owner) { switch (ttypetag(fi)) { case LUA_TCCL: { /* C closure */ CClosure *f = clCvalue(fi); if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) return NULL; /* 'n' not in [1, f->nupvalues] */ *val = &f->upvalue[n-1]; if (owner) *owner = obj2gco(f); return ""; } case LUA_TLCL: { /* Lua closure */ LClosure *f = clLvalue(fi); TString *name; Proto *p = f->p; if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) return NULL; /* 'n' not in [1, p->sizeupvalues] */ *val = f->upvals[n-1]->v; if (owner) *owner = obj2gco(f->upvals[n - 1]); name = p->upvalues[n-1].name; return (name == NULL) ? "(no name)" : getstr(name); } default: return NULL; /* not a closure */ } } LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ lua_lock(L); name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); if (name) { setobj2s(L, L->top, val); api_incr_top(L); } lua_unlock(L); return name; } LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ GCObject *owner = NULL; /* to avoid warnings */ TValue *fi; lua_lock(L); fi = index2value(L, funcindex); api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner); if (name) { L->top--; setobj(L, val, s2v(L->top)); luaC_barrier(L, owner, val); } lua_unlock(L); return name; } static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { LClosure *f; TValue *fi = index2value(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); if (pf) *pf = f; return &f->upvals[n - 1]; /* get its upvalue pointer */ } LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { TValue *fi = index2value(L, fidx); switch (ttypetag(fi)) { case LUA_TLCL: { /* lua closure */ return *getupvalref(L, fidx, n, NULL); } case LUA_TCCL: { /* C closure */ CClosure *f = clCvalue(fi); api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); return &f->upvalue[n - 1]; } default: { api_check(L, 0, "closure expected"); return NULL; } } } LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, int fidx2, int n2) { LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); *up1 = *up2; luaC_objbarrier(L, f1, *up1); }