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 */ }