joe 发表于 2022-6-20 15:53:26

Lua源码分析 - 扩展库篇 - 扩展库Require的实现(12)

目录

一、扩展库 - Lua扩展库的运用

二、扩展库 - 库加载机制luaL_requiref

我们前面几章节讲过,Lua的函数调用有三种类型:C语言闭包函数,C 扩展库API和Lua语言(二进制操作码)。

这一章我们主要讲解一下Lua的扩展库Require的实现。

一、扩展库 - Lua扩展库的运用
我们看几个Lua的示例,一般情况下格式都是:库名称.方法名称(例如:string.find)

//字符串库
string.find("Hello Lua user", "Lua", 1)
string.reverse("Lua")
string.format("the value is:%d",4)

//table表操作
table.insert(fruits,2,"grapes")
table.insert(fruits,"mango")
Lua内置的库,我们在第一章的时候就展示过,可以看一下下面的表格。



二、扩展库 - 库加载机制luaL_requiref
Lua的标准库,在pmain方法中进行初始化加载。pmain方法为Lua运行的核心函数。

/*
** Main body of stand-alone interpreter (to be called in protected mode).
** Reads the options and handles them all.
*/
static int pmain (lua_State *L) {
..省..
    /* 打开常规Lua的标准库 */
luaL_openlibs(L);/* open standard libraries */
..省..
我们主要看一下luaL_openlibs函数的实现:

该函数,主要遍历一个loadedlibs数组,然后循环调用luaL_requiref方法,进行扩展库的逐个加载
loadedlibs数组包含两个元素:扩展库名称(例如:LUA_STRLIBNAME=string),启动方法(例如:luaopen_string)
/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
** LUA标准库常量
**
** 定义标准库名称 & 启动的方法
*/
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
{LUA_BITLIBNAME, luaopen_bit32},
#endif
{NULL, NULL}
};

/**
* 遍历Lua标准库,压入L->top栈
*/
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 */
}
}
luaL_requiref函数定义了模块加载的方法。

该函数首先会在全局注册表G(L)->l_registry上创建一个LUA_LOADED_TABLE的表。如果该表已经创建,则不重复创建。并将该表放入L->top栈顶。
然后通过lua_getfield方法,拿着modname模块名称去取,是否存在该key值。如果不存在,则返回nil,如L->top栈顶。
通过lua_toboolean方法,检查模块是否已经重新加载过。如果没有加载过(nil值返回false),则重新加载模块。
清空栈顶的L->top-1=nil值,并入栈openf和modename,并调用lua_call函数,执行各个模块定义的openf的函数调用。
我们调用lua_call函数的时候,入参个数1&出参个数1,但是我们发现openf函数里面,主要调用luaL_newlib函数,会去创建一个module数组作为返回值,压入栈顶。

然后我们调用lua_pushvalue拷贝一个module的值;然后调用lua_setfield将LOADED值设置为module,并调整堆栈,将栈顶拷贝的module值pop弹出(L->top--)
然后通过lua_remove(L, -2); 方法将LOADED表清除。
最后,我们将该模块module设置到全局环境变量上去。
/*
* 加载系统模块
* modname:模块名称
* openf:回调函数
*/
LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
                               lua_CFunction openf, int glb) {
/* 获取注册表&G(L)->l_registry,如果不存在则创建注册表 */
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
lua_getfield(L, -1, modname);/* LOADED */

/* 如果包没有加载,则重新加载 */
if (!lua_toboolean(L, -1)) {/* package not already loaded? */
    lua_pop(L, 1);/* remove field 栈顶设置为nil */
    lua_pushcfunction(L, openf); /* 将openf设置到栈顶L->top->f的方法上 */
    lua_pushstring(L, modname);/* 将openf设置到栈顶L->top->gc上 */
    lua_call(L, 1, 1);/* call 'openf' to open module */
    lua_pushvalue(L, -1);/* make copy of module (call result) */
    lua_setfield(L, -3, modname);/* LOADED = module */
}
lua_remove(L, -2);/* remove LOADED table */

/* glb=true 则注册到全局表;例如LUA标准库,则会注册到全局表 */
if (glb) {
    lua_pushvalue(L, -1);/* copy of module */
    lua_setglobal(L, modname);/* _G = module */
}
}
画了一张luaL_requiref操作的栈演变过程,帮助大家更好的理解整个栈变更过程。

https://img-blog.csdnimg.cn/20200218135737278.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2luaXRwaHA=,size_16,color_FFFFFF,t_70
————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/104336310

页: [1]
查看完整版本: Lua源码分析 - 扩展库篇 - 扩展库Require的实现(12)