mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-25 21:22:47 +00:00
* Test implementation of lua * Trust that lua knows what its doing with this Silence warnings * actually update top screen when Thingy is called, disable unnecessary ShowPrompt calls * readme * change init for a simple test, print error on top screen too * Readme * change init for a simple test, print error on top screen too * enable more lua libs, edit init.lua with string examples * one more readme edit before bed * Readme * change init for a simple test, print error on top screen too * enable more lua libs, edit init.lua with string examples * make lua a proper file type, add test UI library with two functions, remove luacmd command * remove old attempts at editing lauxlib and liolib * README * README * FS lib, new UI stuff * consistency with "type* ptr" maybe * add custom package searcher, reset package.path * new functions for UI including basic print output buffer, add "Lua scripts..." option to home/power menu * build vram0.tar including subdirs of data * move default path to GM9LUA_DEFAULT_PATH * FS_FileGetData * testfgd, add GM9VERSION global, update README, fix indentation * FS_FileGetData will return a nil instead if it fails * it's actually luaL_pushfail * os * use luaL_tolstring instead of lua_tostring * fix test/remove debugging showprompt * os.clock float attempt * fix print for real * fix swapped offset and size for FileGetData * finish OS stuff * fix os.clock * shorten table in/out * remove .vscode dir * enum test * support building without lua * NO_LUA hides menu options (except when you directly select a lua file) * update UI lib to better match the ideas on #1 * dockermake * add DrawPNG function * whoops its ShowPNG * minor fixes, add DrawPNG * fix AskPrompt, add all showprompts mentioned in #1 * add newly added functions to readme * try to keep separate code and data * update lua to 5.4.7 * remove test libraries now that i want to attempt to implement in a real api * add nix flake for building * add various lua functions, some of them taken from the old attempt but with new names * add dev shell to flake.nix * remove test lua scripts in root * add test lua scripts in data/luascripts * add a whole bunch more lua functions and stuff * add more test lua scripts * add more functions, add preload script, add test io compatibility module * add more functions and test scripts * more functions and stuff * more functions and stuff * more functions and stuff, plus a wip ctrcheck reimpl * yet more functions and stuff * even more functions and stuff * command comparison table.ods * update command comparison table.ods * update command comparison table.ods * Add files via upload * update command comparison table.ods * update ui.show_text to use DrawStringCenter, update ctrcheck rewrite * Split up the ARM9 code (.text, .vectors) and data (.rodata, .data, .bss) sections into their own ELFs. This allows us to use more ARM9 WRAM while leaving the 128k BootROM mirror intact. * use the makefile definition * add title module, move around some functions, update command comparison table * add readme for lua * remove liolib.c and loslib.c * more functions and things, use CheckWritePermissionsLuaError in place of more manual checks, update command comparison table * add missing constant * set CURRDIR to nil instead of "(null)" if not found * remove gm9enum (unused since the restart) * add ui.check_key * split fs module to lua overlay and _fs internal module, and add a check for fs.write_file in lua * add fs.ask_select_file and fs.ask_select_dir * add fs.key_dump, replace overwrite_all and append_all with overwrite and append * add fs.cart_dump and sys.emu_base * add ctrtool, update flake.lock * add io append mode * make sure io.open with write mode starts with an empty file, add os.remove and os.rename aliases * properly implement os.remove compatibility * add fs.verify_with_sha_file, fix PathIsDirectory by using stat instead of opendir * add util.running_as_module (untested) * move scripts over to https://github.com/ihaveamac/GM9-lua-script-experiments * remove ods and dockermake.sh * remove data/scripts * add lua autorun (untested) * fix syntax error * add sys.check_embedded_backup * remove accidental symlink * add sys.check_raw_rtc * fix ui.show_file_text_viewer not freeing memory or reporting an error if OOM happens * add todo notes for ui * work-in-progress lua doc * formatting fix * up heading level for all sections * Revert "up heading level for all sections" This reverts commit 6ef14b619536b4253e341ba40b4dea728358979d. * separators * fix name and error for fs.move * do explicit permission checks in fs.move * fix error string for fs.copy * fix function name for fs.dir_info * fix error string for fs.find and fs.find_not * fix function name for fs.img_umount * partial fs doc * finished fs doc, string fixes for fs module * document fs.cart_dump encrypted opt, remove stat from fs.verify_with_sha_file * title doc * sys doc, error string updates * util doc * add json.lua to lua-doc * add fs.find_all * add 3dstool to flake * make fs.find_all recursive actually recursive, document fs.find_all * add "for" to comparison table * change fs.find to return nil if no path was found, instead of raising an error * change ui.echo to automatically word wrap (untested) * Revert "change ui.echo to automatically word wrap (untested)" This reverts commit 2524e7707708e9818162c31f9f004b6301a3061b. * switch devkitNix to upstream * flake.lock: Update Flake lock file updates: • Updated input 'devkitNix': 'github:ihaveamac/devkitNix/883d173b94e3da8dc4cc0860cdda8c36b738817c' (2024-12-05) → 'github:bandithedoge/devkitNix/95fd44f4ac7cecf24edf22daa899a516df73c6b7' (2025-01-11) • Updated input 'devkitNix/nixpkgs': 'github:NixOS/nixpkgs/566e53c2ad750c84f6d31f9ccb9d00f823165550' (2024-12-03) → 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) • Updated input 'hax-nur': 'github:ihaveamac/nur-packages/c570b3830f7dd4d655afb109300529c896cd8855' (2024-12-05) → 'github:ihaveamac/nur-packages/cd49afba206c2eb10a349d92470fdf2cc942ae23' (2025-01-11) • Updated input 'hax-nur/nixpkgs': 'github:NixOS/nixpkgs/2c15aa59df0017ca140d9ba302412298ab4bf22a' (2024-12-02) → 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/566e53c2ad750c84f6d31f9ccb9d00f823165550' (2024-12-03) → 'github:NixOS/nixpkgs/32af3611f6f05655ca166a0b1f47b57c762b5192' (2025-01-09) * flake.lock: Update Flake lock file updates: • Updated input 'devkitNix': 'github:bandithedoge/devkitNix/95fd44f4ac7cecf24edf22daa899a516df73c6b7' (2025-01-11) → 'github:bandithedoge/devkitNix/a344b0200a044f2d2ff99685f13ff7c53106428e' (2025-02-06) • Updated input 'devkitNix/nixpkgs': 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) → 'github:NixOS/nixpkgs/5b2753b0356d1c951d7a3ef1d086ba5a71fff43c' (2025-02-05) • Updated input 'hax-nur': 'github:ihaveamac/nur-packages/cd49afba206c2eb10a349d92470fdf2cc942ae23' (2025-01-11) → 'github:ihaveamac/nur-packages/2ce890cab4e948109ad1ad82ba18e69240a0d352' (2025-02-06) • Updated input 'hax-nur/nixpkgs': 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) → 'github:NixOS/nixpkgs/8532db2a88ba56de9188af72134d93e39fd825f3' (2025-02-02) • Added input 'hax-nur/treefmt-nix': 'github:numtide/treefmt-nix/bebf27d00f7d10ba75332a0541ac43676985dea3' (2025-01-28) • Added input 'hax-nur/treefmt-nix/nixpkgs': follows 'hax-nur/nixpkgs' • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/32af3611f6f05655ca166a0b1f47b57c762b5192' (2025-01-09) → 'github:NixOS/nixpkgs/5b2753b0356d1c951d7a3ef1d086ba5a71fff43c' (2025-02-05) * make devkitNix and hax-nur inputs follow nixpkgs * Also dump section headers on .dis file * prepare for upstream merge * Restore original README. * Remove flake, was only used for my own testing * fix accidental removal of LIBS * copy lua-doc.md into release archive * README: update to mention Lua in place of GM9Script, add credits * lua-doc: fix typo * add sample HelloScript * lua-doc: remove wip notice, since all gm9script features are replicated * remove accidental inclusion of language.inl * Fix mixture of tabs and spaces * remove accidental nix leftover * re-add @ for add2tar command --------- Co-authored-by: luigoalma <luigoalma@hotmail.com> Co-authored-by: Gruetzig <florianavilov@gmail.com> Co-authored-by: Florian <88926852+Gruetzig@users.noreply.github.com> Co-authored-by: Wolfvak <soherrera1@hotmail.com>
1464 lines
35 KiB
C
1464 lines
35 KiB
C
/*
|
|
** $Id: lapi.c $
|
|
** Lua API
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#define lapi_c
|
|
#define LUA_CORE
|
|
|
|
#include "lprefix.h"
|
|
|
|
|
|
#include <limits.h>
|
|
#include <stdarg.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 "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 (one that is not the 'nilvalue').
|
|
** '!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)
|
|
|
|
|
|
/*
|
|
** Convert an acceptable index to a pointer to its respective value.
|
|
** Non-valid indices return the special nil value 'G(L)->nilvalue'.
|
|
*/
|
|
static TValue *index2value (lua_State *L, int idx) {
|
|
CallInfo *ci = L->ci;
|
|
if (idx > 0) {
|
|
StkId o = ci->func.p + idx;
|
|
api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
|
|
if (o >= L->top.p) return &G(L)->nilvalue;
|
|
else return s2v(o);
|
|
}
|
|
else if (!ispseudo(idx)) { /* negative index */
|
|
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
|
"invalid index");
|
|
return s2v(L->top.p + 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 (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
|
|
CClosure *func = clCvalue(s2v(ci->func.p));
|
|
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
|
|
: &G(L)->nilvalue;
|
|
}
|
|
else { /* light C function or Lua function (through a hook)?) */
|
|
api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
|
|
return &G(L)->nilvalue; /* no upvalues */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** Convert a valid actual index (not a pseudo-index) to its address.
|
|
*/
|
|
l_sinline StkId index2stack (lua_State *L, int idx) {
|
|
CallInfo *ci = L->ci;
|
|
if (idx > 0) {
|
|
StkId o = ci->func.p + idx;
|
|
api_check(L, o < L->top.p, "invalid index");
|
|
return o;
|
|
}
|
|
else { /* non-positive index */
|
|
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
|
"invalid index");
|
|
api_check(L, !ispseudo(idx), "invalid index");
|
|
return L->top.p + idx;
|
|
}
|
|
}
|
|
|
|
|
|
LUA_API int lua_checkstack (lua_State *L, int n) {
|
|
int res;
|
|
CallInfo *ci;
|
|
lua_lock(L);
|
|
ci = L->ci;
|
|
api_check(L, n >= 0, "negative 'n'");
|
|
if (L->stack_last.p - L->top.p > n) /* stack large enough? */
|
|
res = 1; /* yes; check is OK */
|
|
else /* need to grow stack */
|
|
res = luaD_growstack(L, n, 0);
|
|
if (res && ci->top.p < L->top.p + n)
|
|
ci->top.p = L->top.p + 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.p - to->top.p >= n, "stack overflow");
|
|
from->top.p -= n;
|
|
for (i = 0; i < n; i++) {
|
|
setobjs2s(to, to->top.p, from->top.p + i);
|
|
to->top.p++; /* 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.p - L->ci->func.p) + idx;
|
|
}
|
|
|
|
|
|
LUA_API int lua_gettop (lua_State *L) {
|
|
return cast_int(L->top.p - (L->ci->func.p + 1));
|
|
}
|
|
|
|
|
|
LUA_API void lua_settop (lua_State *L, int idx) {
|
|
CallInfo *ci;
|
|
StkId func, newtop;
|
|
ptrdiff_t diff; /* difference for new top */
|
|
lua_lock(L);
|
|
ci = L->ci;
|
|
func = ci->func.p;
|
|
if (idx >= 0) {
|
|
api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
|
|
diff = ((func + 1) + idx) - L->top.p;
|
|
for (; diff > 0; diff--)
|
|
setnilvalue(s2v(L->top.p++)); /* clear new slots */
|
|
}
|
|
else {
|
|
api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
|
|
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
|
}
|
|
api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
|
|
newtop = L->top.p + diff;
|
|
if (diff < 0 && L->tbclist.p >= newtop) {
|
|
lua_assert(hastocloseCfunc(ci->nresults));
|
|
newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
|
|
}
|
|
L->top.p = newtop; /* correct top only after closing any upvalue */
|
|
lua_unlock(L);
|
|
}
|
|
|
|
|
|
LUA_API void lua_closeslot (lua_State *L, int idx) {
|
|
StkId level;
|
|
lua_lock(L);
|
|
level = index2stack(L, idx);
|
|
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
|
|
"no variable to close at given level");
|
|
level = luaF_close(L, level, CLOSEKTOP, 0);
|
|
setnilvalue(s2v(level));
|
|
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.)
|
|
*/
|
|
l_sinline 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.p - 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.p)), 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.p, 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_NUMTYPES, "invalid type");
|
|
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.p, L->top.p - 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.p - 2), s2v(L->top.p - 1), L->top.p - 2);
|
|
L->top.p--; /* 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)) {
|
|
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");
|
|
}
|
|
}
|
|
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.p));
|
|
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 = 0;
|
|
const TValue *o = index2value(L, idx);
|
|
int isnum = tonumber(o, &n);
|
|
if (pisnum)
|
|
*pisnum = isnum;
|
|
return n;
|
|
}
|
|
|
|
|
|
LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) {
|
|
lua_Integer res = 0;
|
|
const TValue *o = index2value(L, idx);
|
|
int isnum = tointeger(o, &res);
|
|
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;
|
|
lua_lock(L);
|
|
o = index2value(L, idx);
|
|
if (!ttisstring(o)) {
|
|
if (!cvt2str(o)) { /* not convertible? */
|
|
if (len != NULL) *len = 0;
|
|
lua_unlock(L);
|
|
return NULL;
|
|
}
|
|
luaO_tostring(L, o);
|
|
luaC_checkGC(L);
|
|
o = index2value(L, idx); /* previous call may reallocate the stack */
|
|
}
|
|
if (len != NULL)
|
|
*len = tsslen(tsvalue(o));
|
|
lua_unlock(L);
|
|
return getstr(tsvalue(o));
|
|
}
|
|
|
|
|
|
LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
|
|
const TValue *o = index2value(L, idx);
|
|
switch (ttypetag(o)) {
|
|
case LUA_VSHRSTR: return tsvalue(o)->shrlen;
|
|
case LUA_VLNGSTR: return tsvalue(o)->u.lnglen;
|
|
case LUA_VUSERDATA: return uvalue(o)->len;
|
|
case LUA_VTABLE: 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 */
|
|
}
|
|
|
|
|
|
l_sinline 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_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
|
|
case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA:
|
|
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.p));
|
|
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.p), 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.p), 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.p, 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.p));
|
|
else {
|
|
TString *ts;
|
|
ts = luaS_new(L, s);
|
|
setsvalue2s(L, L->top.p, 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.p), 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.p -= n;
|
|
while (n--) {
|
|
setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
|
|
/* does not need barrier because closure is white */
|
|
lua_assert(iswhite(cl));
|
|
}
|
|
setclCvalue(L, s2v(L->top.p), cl);
|
|
api_incr_top(L);
|
|
luaC_checkGC(L);
|
|
}
|
|
lua_unlock(L);
|
|
}
|
|
|
|
|
|
LUA_API void lua_pushboolean (lua_State *L, int b) {
|
|
lua_lock(L);
|
|
if (b)
|
|
setbtvalue(s2v(L->top.p));
|
|
else
|
|
setbfvalue(s2v(L->top.p));
|
|
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), p);
|
|
api_incr_top(L);
|
|
lua_unlock(L);
|
|
}
|
|
|
|
|
|
LUA_API int lua_pushthread (lua_State *L) {
|
|
lua_lock(L);
|
|
setthvalue(L, s2v(L->top.p), L);
|
|
api_incr_top(L);
|
|
lua_unlock(L);
|
|
return (G(L)->mainthread == L);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** get functions (Lua -> stack)
|
|
*/
|
|
|
|
|
|
l_sinline 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.p, slot);
|
|
api_incr_top(L);
|
|
}
|
|
else {
|
|
setsvalue2s(L, L->top.p, str);
|
|
api_incr_top(L);
|
|
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
|
}
|
|
lua_unlock(L);
|
|
return ttype(s2v(L->top.p - 1));
|
|
}
|
|
|
|
|
|
/*
|
|
** Get the global table in the registry. Since all predefined
|
|
** indices in the registry were inserted right when the registry
|
|
** was created and never removed, they must always be in the array
|
|
** part of the registry.
|
|
*/
|
|
#define getGtable(L) \
|
|
(&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
|
|
|
|
|
|
LUA_API int lua_getglobal (lua_State *L, const char *name) {
|
|
const TValue *G;
|
|
lua_lock(L);
|
|
G = getGtable(L);
|
|
return auxgetstr(L, G, 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.p - 1), slot, luaH_get)) {
|
|
setobj2s(L, L->top.p - 1, slot);
|
|
}
|
|
else
|
|
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
|
lua_unlock(L);
|
|
return ttype(s2v(L->top.p - 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.p, slot);
|
|
}
|
|
else {
|
|
TValue aux;
|
|
setivalue(&aux, n);
|
|
luaV_finishget(L, t, &aux, L->top.p, slot);
|
|
}
|
|
api_incr_top(L);
|
|
lua_unlock(L);
|
|
return ttype(s2v(L->top.p - 1));
|
|
}
|
|
|
|
|
|
l_sinline int finishrawget (lua_State *L, const TValue *val) {
|
|
if (isempty(val)) /* avoid copying empty items to the stack */
|
|
setnilvalue(s2v(L->top.p));
|
|
else
|
|
setobj2s(L, L->top.p, val);
|
|
api_incr_top(L);
|
|
lua_unlock(L);
|
|
return ttype(s2v(L->top.p - 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.p - 1));
|
|
L->top.p--; /* 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.p, 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.p, 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.p));
|
|
t = LUA_TNONE;
|
|
}
|
|
else {
|
|
setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
|
|
t = ttype(s2v(L->top.p));
|
|
}
|
|
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.p - 1));
|
|
L->top.p--; /* pop value */
|
|
}
|
|
else {
|
|
setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
|
|
api_incr_top(L);
|
|
luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
|
|
L->top.p -= 2; /* pop value and key */
|
|
}
|
|
lua_unlock(L); /* lock done by caller */
|
|
}
|
|
|
|
|
|
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
|
const TValue *G;
|
|
lua_lock(L); /* unlock done in 'auxsetstr' */
|
|
G = getGtable(L);
|
|
auxsetstr(L, G, 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.p - 2), slot, luaH_get)) {
|
|
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
|
}
|
|
else
|
|
luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
|
|
L->top.p -= 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.p - 1));
|
|
}
|
|
else {
|
|
TValue aux;
|
|
setivalue(&aux, n);
|
|
luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
|
|
}
|
|
L->top.p--; /* pop value */
|
|
lua_unlock(L);
|
|
}
|
|
|
|
|
|
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
|
Table *t;
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
t = gettable(L, idx);
|
|
luaH_set(L, t, key, s2v(L->top.p - 1));
|
|
invalidateTMcache(t);
|
|
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
|
L->top.p -= n;
|
|
lua_unlock(L);
|
|
}
|
|
|
|
|
|
LUA_API void lua_rawset (lua_State *L, int idx) {
|
|
aux_rawset(L, idx, s2v(L->top.p - 2), 2);
|
|
}
|
|
|
|
|
|
LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) {
|
|
TValue k;
|
|
setpvalue(&k, cast_voidp(p));
|
|
aux_rawset(L, idx, &k, 1);
|
|
}
|
|
|
|
|
|
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.p - 1));
|
|
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
|
L->top.p--;
|
|
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.p - 1)))
|
|
mt = NULL;
|
|
else {
|
|
api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
|
|
mt = hvalue(s2v(L->top.p - 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.p--;
|
|
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.p - 1));
|
|
luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
|
|
res = 1;
|
|
}
|
|
L->top.p--;
|
|
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.p - L->top.p >= (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.p - (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.p - (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.p - 1)); /* get new function */
|
|
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
|
/* get global table from registry */
|
|
const TValue *gt = getGtable(L);
|
|
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
|
setobj(L, f->upvals[0]->v.p, 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.p - 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);
|
|
if (g->gcstp & GCSTPGC) /* internal stop? */
|
|
return -1; /* all options are invalid when stopped */
|
|
lua_lock(L);
|
|
va_start(argp, what);
|
|
switch (what) {
|
|
case LUA_GCSTOP: {
|
|
g->gcstp = GCSTPUSR; /* stopped by the user */
|
|
break;
|
|
}
|
|
case LUA_GCRESTART: {
|
|
luaE_setdebt(g, 0);
|
|
g->gcstp = 0; /* (GCSTPGC must be already zero here) */
|
|
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 oldstp = g->gcstp;
|
|
g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */
|
|
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->gcstp = oldstp; /* 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 = gcrunning(g);
|
|
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) {
|
|
TValue *errobj;
|
|
lua_lock(L);
|
|
errobj = s2v(L->top.p - 1);
|
|
api_checknelems(L, 1);
|
|
/* error object is the memory error message? */
|
|
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
|
|
luaM_error(L); /* raise a memory error */
|
|
else
|
|
luaG_errormsg(L); /* raise a regular error */
|
|
/* 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.p - 1);
|
|
if (more) {
|
|
api_incr_top(L);
|
|
}
|
|
else /* no more elements */
|
|
L->top.p -= 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->tbclist.p < o, "given index below or equal a marked 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 > 0)
|
|
luaV_concat(L, n);
|
|
else { /* nothing to concatenate */
|
|
setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
|
|
api_incr_top(L);
|
|
}
|
|
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.p, 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.p), 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_VCCL: { /* 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_VLCL: { /* 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.p;
|
|
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.p, 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.p--;
|
|
setobj(L, val, s2v(L->top.p));
|
|
luaC_barrier(L, owner, val);
|
|
}
|
|
lua_unlock(L);
|
|
return name;
|
|
}
|
|
|
|
|
|
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
|
|
static const UpVal *const nullup = NULL;
|
|
LClosure *f;
|
|
TValue *fi = index2value(L, fidx);
|
|
api_check(L, ttisLclosure(fi), "Lua function expected");
|
|
f = clLvalue(fi);
|
|
if (pf) *pf = f;
|
|
if (1 <= n && n <= f->p->sizeupvalues)
|
|
return &f->upvals[n - 1]; /* get its upvalue pointer */
|
|
else
|
|
return (UpVal**)&nullup;
|
|
}
|
|
|
|
|
|
LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
|
|
TValue *fi = index2value(L, fidx);
|
|
switch (ttypetag(fi)) {
|
|
case LUA_VLCL: { /* lua closure */
|
|
return *getupvalref(L, fidx, n, NULL);
|
|
}
|
|
case LUA_VCCL: { /* C closure */
|
|
CClosure *f = clCvalue(fi);
|
|
if (1 <= n && n <= f->nupvalues)
|
|
return &f->upvalue[n - 1];
|
|
/* else */
|
|
} /* FALLTHROUGH */
|
|
case LUA_VLCF:
|
|
return NULL; /* light C functions have no upvalues */
|
|
default: {
|
|
api_check(L, 0, "function 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);
|
|
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
|
|
*up1 = *up2;
|
|
luaC_objbarrier(L, f1, *up1);
|
|
}
|
|
|
|
|