`

LUA源码分析七:require的函数调用堆栈

    博客分类:
  • LUA
阅读更多

require的调用其实很简单,熟悉完env的设置后,其实本质上都是走luaL_dofile函数对全局表的设置。do_file完,然后设置环境变量。借助此,把LUA里的函数堆栈方式依次跟调一次。


如果是熟悉汇编堆栈的形式,对LUA的源码风格很好理解。比如没有实际的变量名,通过对栈的偏移来访问。大于0的表示从base基地址加起,负数的表示从top往后减,或者是表示特定的全局值。因为有这种访问上的规则,所以lua的源码对访问封装的层次很清晰,哪里要回退栈指针,哪里要增加top地址等等。


static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  int i;

  lua_settop(L, 1);  /* _LOADED table will be at index 2 */
  lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
  lua_getfield(L, 2, name);


  if (lua_toboolean(L, -1)) {  /* is it there? */
    if (lua_touserdata(L, -1) == sentinel)  /* check loops */
      luaL_error(L, "loop or previous error loading module " LUA_QS, name);
    return 1;  /* package is already loaded */
  }
  /* else must load it; iterate over available loaders */
  lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.loaders") " must be a table");
  lua_pushliteral(L, "");  /* error message accumulator */

/*
至此栈的数据
0x00395108:LUA_REGISTRYINDEX, "_LOADED"
0x00395118:lua_getfield(L, 2, name)  //name,导入的模块名
0x00395128:LUA_ENVIRONINDEX, "loaders"
0x00395138:""
*/
  for (i=1; ; i++) {
/*
i=1:
从0x00395128取值赋到栈顶
0x00395148:LUA_ENVIRONINDEX, "loaders"[1]

i=2:
从0x00395128取值赋到栈顶
0x00395148:LUA_ENVIRONINDEX, "loaders"[2]

*/
    lua_rawgeti(L, -2, i);  /* get a loader */
    if (lua_isnil(L, -1))
      luaL_error(L, "module " LUA_QS " not found:%s",
                    name, lua_tostring(L, -2));
/*
i=1:
0x00395158:lua_pushstring(L, name);

i=2:
0x00395158:lua_pushstring(L, name);
*/
    lua_pushstring(L, name);
/*
i=1:
呼叫0x00395148的函数,name做为参数。呼叫过程见段1

i=2:
呼叫0x00395148的函数,name做为参数。呼叫过程见段2
*/
    lua_call(L, 1, 1);  /* call it */
/*
i=1:
判断是否找到模块名
*/
    if (lua_isfunction(L, -1))  /* did it find module? */
      break;  /* module loaded successfully */
    else if (lua_isstring(L, -1))  /* loader returned error message? */
      lua_concat(L, 2);  /* accumulate it */
    else
      lua_pop(L, 1);
  }
/*
0x00395158:sentinel
*/
  lua_pushlightuserdata(L, sentinel);
/*
基地址是L->base = 0x003950f8,
取到0x00395108:LUA_REGISTRYINDEX, "_LOADED"值。
把top的值(sentinel)给_LOADED[name],同时top--
*/
  lua_setfield(L, 2, name);  /* _LOADED[name] = sentinel */
/*
0x00395158:lua_pushstring(L, name)
*/
  lua_pushstring(L, name);  /* pass name as argument to module */
/*
  调用,将模块环境载入.并且修改了0x00395148函数块,压入一个lua的函数调用结构
*/
  lua_call(L, 1, 1);  /* run loaded module */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 2, name);  /* _LOADED[name] = returned value */
/*
0x00395158:lua_pushstring(L, name)
接下来就是善后工作了。注意,_LOADED[name]被设置为true。
*/
  lua_getfield(L, 2, name);
  if (lua_touserdata(L, -1) == sentinel) {   /* module did not set a value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_pushvalue(L, -1);  /* extra copy to be returned */
    lua_setfield(L, 2, name);  /* _LOADED[name] = true */
  }
  return 1;
}
 

/*
段1
*/
static int loader_preload (lua_State *L) {
/*
取得0x00395158:lua_pushstring(L, name);
*/
  const char *name = luaL_checkstring(L, 1);
/*
0x00395168:LUA_ENVIRONINDEX, "preload"
*/
  lua_getfield(L, LUA_ENVIRONINDEX, "preload");
  if (!lua_istable(L, -1))
    luaL_error(L, LUA_QL("package.preload") " must be a table");
/*
0x00395178:从0x00395168LUA_ENVIRONINDEX, "preload"表里,读取name的字段

*/
  lua_getfield(L, -1, name);
/*
 如果是自定义的,则没找到,压入字符串,供上层判断
*/
  if (lua_isnil(L, -1))  /* not found? */
    lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
  return 1;
}
 
/*
段2
*/
static int loader_Lua (lua_State *L) {
  const char *filename;
  const char *name = luaL_checkstring(L, 1);
/*
查找路径的规则。如果想知道具体没必要细跟。可以设置一个找不到模块名,
查看返回的错误信息(带有所有路径名的搜索)即可。
*/
  filename = findfile(L, name, "path");
  if (filename == NULL) return 1;  /* library not found in this path */
/*
luaL_loadfile很熟悉了
*/
  if (luaL_loadfile(L, filename) != 0)
    loaderror(L, filename);
  return 1;  /* library loaded successfully */
}
 
1
3
分享到:
评论

相关推荐

    Lua程序开发

    - **调用堆栈**:Lua使用调用堆栈来管理函数调用,C/C++需要理解并操作这个堆栈来实现函数调用和返回。 3. **Lua程序设计** - **基本数据类型**:包括数字(整数和浮点数)、字符串、布尔值、表(类似数组和哈希...

    lua api lua文档

    - **堆栈**:Lua虚拟机中的核心数据结构,用于管理函数调用。 - **CAPI的错误处理**:如何处理Lua C API中可能出现的错误。 以上内容覆盖了Lua语言的基础概念、高级特性以及标准库的使用方法,对于学习和使用Lua都...

    Lua_API.rar_lua_lua api

    `debug`库提供了用于调试的工具,如跟踪调用堆栈、获取和设置变量等。 7. **内存管理**:Lua自动管理内存,但也可以通过`collectgarbage()`函数手动触发垃圾回收。 8. **C接口**:Lua提供了一个C API,允许C/C++...

    LUA程序设计参考文档

    LUA的模块系统允许将代码组织成独立的模块,通过require函数导入和使用。模块通常以`.lua`文件形式存在,可以使用dofile或loadfile来执行。 四、错误处理与调试 LUA提供了error函数用于抛出错误,以及pcall和xpcall...

    Lua 编程指南

    1. 模块:Lua通过`require`函数加载模块,模块通常是一个返回值为表的文件,表中的元素对外提供模块的功能。 2. 包:Lua的包系统管理模块,通过`package`库提供模块路径搜索、加载缓存等功能。 ### 五、错误处理与...

    LuaPanda-master.zip

    3. **调用堆栈**:显示函数调用的层级关系,帮助追踪问题的来源。 4. **单步执行**:支持逐行执行,观察代码执行过程,便于理解代码逻辑。 5. **条件断点**:设置特定条件的断点,只有满足条件时才会暂停执行。 6...

    lua-compat-5.3:兼容性模块,为Lua 5.2和5.1提供Lua-5.3风格的API

    7. **新的错误处理机制**:`error`函数现在可以接受一个额外的层次参数,用于控制错误处理的堆栈跟踪。 `lua-compat-5.3`模块实现了上述特性,并在Lua 5.2和5.1环境中提供,使得开发者能够在旧版本的Lua中使用5.3的...

    lua程序设计

    - **回跟踪**:记录错误发生的调用堆栈。 #### 九、协同程序 ##### 9.1 协同的基础 - **定义**:一种轻量级的并发模型。 - **特点**:协程可以暂停执行,将控制权交还给调用者,之后可以从暂停的地方恢复执行。 #...

    lua-snip:各种lua片段

    lua片段每个片段返回一个函数,所以只require为任何你想要调用该函数的文件。 local split = require "string.split"文献资料以格式编写: variablename:TYPE @DEFAULT = DESCRIPTION调试findlocal(loc,stackmod)...

    LUA编程文档资料查询

    元方法是在特定操作(如索引、调用)发生时调用的函数。 6. **垃圾回收** LUA使用引用计数和可达性分析相结合的垃圾回收策略,自动管理内存,开发者无需手动释放内存。 7. **协程(Coroutines)** LUA提供轻量级...

    Programming in Lua

    require函数用于加载和执行Lua模块。 #### 8.2 C模块 C模块是用C语言编写的,可以在Lua中调用,用于访问C库或提高性能。 #### 8.3 错误 Lua中的错误可以通过assert函数引发,用于检查假设条件是否满足。 #### ...

    lua 基础教程

    注意,函数调用完成后,返回值会被压入堆栈,需要手动处理。 4. **Lua 语法规则** - **变量**:Lua是动态类型语言,变量无需声明类型,直接赋值即可。 - **数据类型**:包括nil、boolean、number、string、table...

    Lua中文教程(pdf版)

    8.1 require函数.................49 8.2 C Packages.................50 8.3 错误........51 8.4 异常和错误处理.......52 8.5 错误信息和回跟踪(Tracebacks)....53 第9章协同程序................56 9.1 ...

    scriptlib:lua脚本的公共图书馆

    在Lua中,函数是一等公民,`scriptlib`可能会包含大量用于处理各种任务的函数,如数据操作、字符串处理、文件I/O、网络通信等。这些函数遵循 Lua 的编程风格,注重代码的简洁性和可读性。 例如,`scriptlib`可能...

    爱情1618

    Lua的模块系统通过require函数实现,允许将代码组织成多个独立的模块,提高代码的可维护性和重用性。在"爱情1618"项目中,可能存在着多个lua文件,每个文件代表一个特定的功能模块,如角色控制、物品系统、地图管理...

    app-ferre

    通过require函数,我们可以按需加载和使用其他模块,这有助于保持代码的整洁和模块化。同时,Lua的模块可以使用C++实现,这为优化性能提供了可能。在"app-ferre"中,可能会有一些性能敏感的部分是以C++编写的库,...

    DevProxFiles

    `require`函数可以用来加载和执行模块。 4. **C API**:Lua提供了C接口,允许C/C++程序与Lua交互。通过这个接口,可以将C/C++编写的库或功能暴露给Lua脚本使用。 5. **嵌入式编程**:Lua因其小巧的体积和易于集成...

    微妙的

    9. **C接口**:Lua提供了与C语言交互的接口,允许在Lua中调用C函数或用C编写扩展。这在性能优化和接入底层资源时非常有用,但C接口的使用需要遵循特定的规范,否则可能引入安全问题。 10. **协同程序(Coroutines)...

Global site tag (gtag.js) - Google Analytics