初始化lua环境

  • lua文件顶层函数:写在lua文件中的、顶层脚本函数。没有function关键字,没有参数,可以有return。lua_load加载lua文件后,会自动将这个函数压栈。

在app启动阶段,base_instance::initialize()会构造lua_kernel_base。

 

一、lua_kernel_base::lua_kernel_base

lua_kernel_base依次执行5个任务。

  1. 加载lua内置的共享库、函数。
  2. 为后绪错误处理作准备。
  3. 向注册表添加两个元表;gettext、translatable string。
  4. 新增_ENV.ros表,向该表增加函数、常量。
  5. 加载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.tableluaopen_table全部
_ENV.stringluaopen_string全部
_ENV.mathluaopen_math全部
_ENV.coroutineluaopen_coroutine全部
_ENV.debugluaopen_debug只留traceback, getinfo
_ENV.osluaopen_os只留clock, date, time, difftime
_ENV.utf8luaopen_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类型
4stringlua/package.lua
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/package.lua
	lua_rawget(L, -2);
	if(!lua_isnil(L, -1)) {
                package表中存在key="lua/package.lua"的记录,表示已加载过个lua文件,返回加载成功。
		return 1;
	}
4 nil
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/package.lua
	lua_pop(L, 1);
	lua_pushvalue(L, 1);
4stringlua/package.lua
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/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文件顶层函数。

5functionlua文件顶层函数
4stringlua/package.lua
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/package.lua

但执行完lua_load后,lua_fileops::load_file会调用lua_remove(L, -2),即删除stkidx=-2的那个单元。

4functionlua文件顶层函数
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/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,它会把被调函数、参数弹出栈。

4tablelua文件顶层函数返回的empty_pkg
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/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。

6tablelua文件顶层函数返回的empty_pkg
5stringlua/package.lua
4tablelua文件顶层函数返回的empty_pkg
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/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。

4tablelua文件顶层函数返回的empty_pkg
3table_ENV.rose.package记录的值
2table_ENV.rose
1stringlua/package.lua
	// stack is now [packagename] [rose] [package] [results]
	VALIDATE(lua_gettop(L) == 4, null_str);
	lua_pop(L, 4);
	return 1;
}

全部评论: 0

    写评论: