- 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; }