之前看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,而不是直接调。