您的位置:首页 > 编程语言 > Lua

lua5.3源码基础阅读(luaL_openlibs)

2016-04-01 20:17 302 查看
###C库结构和导入到lua

导入全局性的库到lua中,这些库由C实现:

/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
//....
{NULL,NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib;
/* "require" functions from 'loadedlibs' and set results to global table */
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1);  /* remove lib */
}
}


每一个库封装了很多函数, 且每个库都由库名和open函数导入,如以协程人库为例:

{LUA_COLIBNAME, luaopen_coroutine},


通过看协程的库的创建过程可以知道如何将C函数写的库导入lua:

// 每个库必须有的open函数:luaopen_name(..)
// newlib的实现就是一个table
LUAMOD_API int luaopen_coroutine (lua_State *L) {
luaL_newlib(L, co_funcs);
return 1;
}
//下面是协程库的lua函数名和对应的C函数:
static const luaL_Reg co_funcs[] = {
{"create", luaB_cocreate},
{"resume", luaB_coresume},
{"running", luaB_corunning},
{"status", luaB_costatus},
{"wrap", luaB_cowrap},
{"yield", luaB_yield},
{"isyieldable", luaB_yieldable},
{NULL, NULL}
};


lua_newlib(L,l)

单个C函数组成的库的open函数里, 调用的是下面的newlib函数
luaL_newlib(L, co_funcs);
, 实现如下:

// 根据库函数数组 luaL_Reg的大小创建的table, 这里的createtable的实现就是在栈中创建一个哈希表, 表元素个数为sizeof(l)/sizeof((l)[0]) - 1
#define luaL_newlibtable(L,l)  \
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
//
// 库的实现就是以l的大小创建了一个table
#define luaL_newlib(L,l)  \
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))


下面是上面调用的luaL_setfuncs函数的实现代码, 由于当前的栈顶是刚才newlibtable出来的table, 所以现在是将库函数名set到table中;

下面的实现可以看出, 每个函数创建的闭包前都先复制了一份闭包, 这样所有的库函数的闭包是一样的;

checkstack函数是检查当前的栈空间是否足够放得下nup个闭包元素;

lua_pushvalue()就是将索引处的值复制到栈顶

/*
** set functions from list 'l' into table at top - 'nup'; each
** function gets the 'nup' elements at the top as upvalues.
** Returns with only the table at the stack.
*/
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {  // nup是闭包元素的个数
luaL_checkstack(L, nup, "too many upvalues");   // 如果空间不够会自动扩展栈空间
for (; l->name != NULL; l++) {  /* fill the table with given functions */
int i;
for (i = 0; i < nup; i++)  /* copy upvalues to the top */
lua_pushvalue(L, -nup);     // 压入所有的闭包, 当前栈顶(新的table)下的元素是nup个的闭包
lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */
lua_setfield(L, -(nup + 2), l->name);
}
lua_pop(L, nup);  /* remove upvalues */
}


下面看下luaL_checkstack调用的check函数, ci为当前栈中的调用的函数帧, 可以看成函数的局部空间, ci->func为底, ci->top为空间顶, 两者之间就是当前函数的局部空间:

//  const int extra = LUA_MINSTACK;  5个额外的空间
// 调用的是该: lua_checkstack(L, space + extra)) ..
LUA_API int lua_checkstack (lua_State *L, int n) {
int res;
CallInfo *ci = L->ci;       // 当前的函数调用帧, ci->func为函数调用点
lua_lock(L);
api_check(L, n >= 0, "negative 'n'");
if (L->stack_last - L->top > n)  /* stack large enough? */
res = 1;  /* yes; check is OK */     //空间足够
else {  /* no; need to grow stack */      // 空间不够,需要增加栈空间
int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
if (inuse > LUAI_MAXSTACK - n)  /* can grow without overflow? */
res = 0;  /* no */
else  /* try to grow stack */
res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK);
}
if (res && ci->top < L->top + n)
ci->top = L->top + n;  /* adjust frame top */      // 调用帧顶为栈顶+所需空间
lua_unlock(L);
return res;
}


luaL_requiref

前面的库导入过程中,luaL_requiref是真正的导入函数,实现如下, 说明符中将该函数表示为简化版的require, 完成了三件事:

1. 在全局表的_LOADED中查找有没有modulename对应的值;

2. 没有则压入库open函数和库名, 并调用open函数在栈中创建table, 将该table以key为modulename存入_LOADED全局表中;

3. 如果glb为真,即要求放入全局表中, 则放如全局, _G[modulename] = module

该函数的功能和require的功能类似, 都会检查_loaded表, 没有则导入到表中取, 一共使用;

/*
** Stripped-down 'require': After checking "loaded" table, calls 'openf'
** to open a module, registers the result in 'package.loaded' table and,
** if 'glb' is true, also registers the result in the global table.
** Leaves resulting module on the top.
*/
LUALIB_API void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb)
{
// 全局注册表找到_loaded表
luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, modname);  /* _LOADED[modname] */
if (!lua_toboolean(L, -1)) {  /* package not already loaded? */
lua_pop(L, 1);  /* remove field */
lua_pushcfunction(L, openf);
lua_pushstring(L, modname);  /* argument to open function */
//调用库的open函数,在栈中创建了一个table
lua_call(L, 1, 1);  /* call 'openf' to open module */
// 复制一份以保存到_loaded里面取
lua_pushvalue(L, -1);  /* make copy of module (call result) */
lua_setfield(L, -3, modname);  /* _LOADED[modname] = module */
}
lua_remove(L, -2);  /* remove _LOADED table */
if (glb) {
//   复制一份保存到_G里面去
lua_pushvalue(L, -1);  /* copy of module */
lua_setglobal(L, modname);  /* _G[modname] = module */          是_G
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lua 源码