risc-v中文社区

 找回密码
 立即注册
查看: 1539|回复: 0

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

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2022-6-20 15:53:26 | 显示全部楼层 |阅读模式
目录

一、扩展库 - 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[modname]值设置为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[modname] */

  /* 如果包没有加载,则重新加载 */
  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[modname] = 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[modname] = module */
  }
}
画了一张luaL_requiref操作的栈演变过程,帮助大家更好的理解整个栈变更过程。


————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/104336310

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-4-20 00:57 , Processed in 0.021092 second(s), 18 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表