- lua文件顶层函数:写在lua文件中的、顶层脚本函数。没有function关键字,没有参数,可以有return。lua_load加载lua文件后,会自动将这个函数压栈。
在app启动阶段,base_instance::initialize()会构造lua_kernel_base。
一、lua_kernel_base::lua_kernel_base
lua_kernel_base依次执行5个任务。
- 加载lua内置的共享库、函数。
- 为后绪错误处理作准备。
- 向注册表添加两个元表;gettext、translatable string。
- 新增_ENV.ros表,向该表增加函数、常量。
- 加载lua/package.lua,添加rose.require方法,为后绪加载*.lua文件准备。
lua_kernel_base::lua_kernel_base() : mState(luaL_newstate()) { get_lua_kernel_base_ptr(mState) = this; lua_State *L = mState; static const luaL_Reg safe_libs[] { { "", luaopen_base }, { "table", luaopen_table }, { "string", luaopen_string }, { "math", luaopen_math }, { "coroutine", luaopen_coroutine }, { "debug", luaopen_debug }, { "os", luaopen_os }, { "utf8", luaopen_utf8 }, // added in Lua 5.3 { nullptr, nullptr } }; for (luaL_Reg const *lib = safe_libs; lib->func; ++lib) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } VALIDATE(lua_gettop(L) == 0, null_str); // Disable functions from os which we don't want. lua_getglobal(L, "os"); lua_pushnil(L); while(lua_next(L, -2) != 0) { lua_pop(L, 1); ...... lua_setfield(L, -3, function); } lua_pop(L, 1); // Disable functions from debug which we don't want. lua_getglobal(L, "debug"); lua_pushnil(L); while(lua_next(L, -2) != 0) { lua_pop(L, 1); ...... lua_setfield(L, -3, function); } lua_pop(L, 1); VALIDATE(lua_gettop(L) == 0, null_str);
向_ENV增加lua内置的共享库。并按自个需要,减少debug、os表提供的函数。
_ENV.table | luaopen_table | 全部 |
_ENV.string | luaopen_string | 全部 |
_ENV.math | luaopen_math | 全部 |
_ENV.coroutine | luaopen_coroutine | 全部 |
_ENV.debug | luaopen_debug | 只留traceback, getinfo |
_ENV.os | luaopen_os | 只留clock, date, time, difftime |
_ENV.utf8 | luaopen_utf8 | 全部 |
// Delete dofile, loadfile, print. lua_pushnil(L); lua_setglobal(L, "dofile"); lua_pushnil(L); lua_setglobal(L, "loadfile"); lua_pushnil(L); lua_setglobal(L, "print"); lua_pushcfunction(L, intf_load); lua_setglobal(L, "load"); lua_pushcfunction(L, intf_loadstring); lua_setglobal(L, "loadstring"); VALIDATE(lua_gettop(L) == 0, null_str);
按自个需要,调整一些_ENV函数。
_ENV.loadfile | 删除 | nil |
_ENV.dofile | 删除 | nil |
_ENV.print | 删除 | nil。输出日志用rose.log("hello") |
_ENV.load | 重指向 | intf_load |
_ENV.loadstring | 重指向 | intf_loadstring |
push_error_handler(L); VALIDATE(lua_gettop(L) == 0, null_str);
向注册表增加通用错误处理操作。
// Create the gettext metatable. lua_common::register_gettext_metatable(L); // Create the tstring metatable. lua_common::register_tstring_metatable(L); VALIDATE(lua_gettop(L) == 0, null_str);
向注册表添加两个元表;gettext、translatable string。
// Add some callback from the rose lib SDL_Log("Registering basic rose API..."); static luaL_Reg const callbacks[] { { "log", &intf_log}, ...... { nullptr, nullptr } }; lua_getglobal(L, "rose"); VALIDATE(lua_isnil(L, -1), null_str); // this nil is pushed lua_pop(L, 1); // pop nil lua_newtable(L); luaL_setfuncs(L, callbacks, 0); lua_setglobal(L, "rose"); register_rose_const_value(); VALIDATE(lua_gettop(L) == 0, null_str);
新增_ENV.ros表,向该表增加函数、常量。
SDL_Log("Initializing package repository..."); // Create the package table. lua_getglobal(L, "rose"); lua_newtable(L); lua_setfield(L, -2, "package"); lua_pop(L, 1); VALIDATE(lua_gettop(L) == 0, null_str); lua_pushstring(L, "lua/package.lua"); int res = intf_require(L); VALIDATE(res == 1, "Error: Failed to load lua/package.lua");
在_ENV.rose新增一条记录{package, {}},key=package将用于存储lua_load过的*.lua文件顶层函数的返回结果
VALIDATE(lua_gettop(L) == 0, null_str); }
二、lua_kernel_base::intf_require
加载stkidx=1处指定的lua文件,并执行该lua的顶层脚本函数。为更直观,以要加载“lua/package.lua”为例,即下面变量m是“lua/package.lua”。
int lua_kernel_base::intf_require(lua_State* L) { tstack_size_lock lock(L, -1); const char * m = luaL_checkstring(L, 1); if(!m) { return luaL_argerror(L, 1, "found a null string argument to rose require"); } // Check if there is already an entry. // 把_ENV.rose表入栈 lua_getglobal(L, "rose"); // 把字符串“package”入栈。 lua_pushstring(L, "package"); // 以key="package",在_ENV.rose中查找value,这个value的类型是table。 // 对第一次调用的inf_require的lua/package.lua来说,得到的value是个空表。 lua_rawget(L, -2); lua_pushvalue(L, 1);
此时栈情况
stdidx | 类型 | 值 |
4 | string | lua/package.lua |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
lua_rawget(L, -2); if(!lua_isnil(L, -1)) { package表中存在key="lua/package.lua"的记录,表示已加载过个lua文件,返回加载成功。 return 1; }
4 | nil | |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
lua_pop(L, 1); lua_pushvalue(L, 1);
4 | string | lua/package.lua |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
if(lua_fileops::load_file(L) != 1) { // should end with the file contents loaded on the stack. actually it will call lua_error otherwise, // the return 0 is redundant. // stack is now [packagename] [rose] [package] [chunk] return 0; }
lua_fileops::load_file从stkidx=-1取出要读取的lua文件,以它为参数调用lua_load,生成lua脚本函数原型Proto等内容,结果存放在L。经过lua_load后,栈中多了一条记录,类型是function,指向lua文件顶层函数。
5 | function | lua文件顶层函数 |
4 | string | lua/package.lua |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
但执行完lua_load后,lua_fileops::load_file会调用lua_remove(L, -2),即删除stkidx=-2的那个单元。
4 | function | lua文件顶层函数 |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
if (!this->protected_call(L, 0, 1, std::bind(&lua_kernel_base::log_error, this, _1, _2))) { // historically if rose.require fails it just yields nil and some logging messages, not a lua error return 0; }
执行lua/package.lua的顶层lua脚本函数,默认认为这顶层函数有一个返回值。protected_cal会调用lua_pcall,它会把被调函数、参数弹出栈。
4 | table | lua文件顶层函数返回的empty_pkg |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
// stack is now [packagename] [rose] [package] [results] lua_pushvalue(L, 1); lua_pushvalue(L, -2);
返回值empty_pkg已经在栈中,为什么还要调用这两个lua_pushvalue?——顺序不对,要让lua_settable生成记录,必须先入栈key、再是value。
6 | table | lua文件顶层函数返回的empty_pkg |
5 | string | lua/package.lua |
4 | table | lua文件顶层函数返回的empty_pkg |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
// stack is now [packagename] [rose] [package] [results] [packagename] [results] // Add the return value to the table. lua_settable(L, -4);
向rose.package表添加一条记录:{"lua/package.lua" empty_pkg},同时从栈中弹出key、value。
4 | table | lua文件顶层函数返回的empty_pkg |
3 | table | _ENV.rose.package记录的值 |
2 | table | _ENV.rose |
1 | string | lua/package.lua |
// stack is now [packagename] [rose] [package] [results] VALIDATE(lua_gettop(L) == 4, null_str); lua_pop(L, 4); return 1; }