之前看luaL_openlibs(),感觉直接调打开库的函数好像也没差别,所以将
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}
改成了
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lib->func(L);
}
}
一直也没出啥问题,直到最近碰到一段类似这样的程序:
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if(luaL_dofile(L, "test.lua") != 0)
{
const char *perr = lua_tostring(L, -1);
fprintf(stderr, "lua pcall error: %s\n", perr);
return 1;
}
lua_getglobal(L, "size");
if(lua_pcall(L, 0, 1, 0))
{
const char *perr = lua_tostring(L, -1);
fprintf(stderr, "lua pcall error: %s\n", perr);
return 1;
}
int size = (int)lua_tonumber(L, 1);
lua_pop(L, 1);
printf("%d\n", size);
return 0;
}
test.lua:
function size()
return 1,2,3
end
运行时报“PANIC: unprotected error in call to Lua API (no calling environment)”。当idx是LUA_ENVIRONINDEX时,lua_replace()需要有调用栈。
LUA_API void lua_replace (lua_State *L, int idx) {
StkId o;
lua_lock(L);
/* explicit test for incompatible code */
if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)
luaG_runerror(L, "no calling environment");
api_checknelems(L, 1);
o = index2adr(L, idx);
api_checkvalidindex(L, o);
if (idx == LUA_ENVIRONINDEX) {
Closure *func = curr_func(L);
api_check(L, ttistable(L->top - 1));
func->c.env = hvalue(L->top - 1);
luaC_barrier(L, func, L->top - 1);
}
else {
setobj(L, o, L->top - 1);
if (idx < LUA_GLOBALSINDEX) /* function upvalue? */
luaC_barrier(L, curr_func(L), L->top - 1);
}
L->top--;
lua_unlock(L);
}
把导致报错的luaopen_package和luaopen_io去掉后再运行,得到的结果却是0。
因为我瞎改了一些代码,最初怀疑是不是调用过程出问题了,栈让我改坏了。但最后却发现问题出在int size = (int)lua_tonumber(L, 1);
上,1表示从栈底取数据,实际应该用-1从栈顶取数据。
调luaL_openlibs(L)之前栈是空的,lua_call(L, 1, 0)也没在栈上留东西,而所以栈上只有一个值,从栈顶栈底取数据都一样,但lib->func(L)却在栈上遗留了数据。
最后看看为什么lua解释器不会报“PANIC: unprotected error in call to Lua API (no calling environment)”,因为在调luaL_openlibs(L)之前,已经有了一层调用:
int main (int argc, char **argv) {
int status;
struct Smain s;
lua_State *L = luaL_newstate();
if (L == NULL) {
l_message(argv[0], "cannot create state: not enough memory");
return EXIT_FAILURE;
}
s.argc = argc;
s.argv = argv;
status = lua_cpcall(L, pmain, &s);
lua_close(L);
return status ? EXIT_FAILURE : EXIT_SUCCESS;
}
pmain里再去掉luaL_openlibs(L),所以也理解了为什么它用lua_cpcall包了一层pmain,而不是直接调。