随笔 - 17  文章 - 48  trackbacks - 0
<2022年8月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(3)

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

很早就想读lua的源码,也曾很多次浏览过大概。不过我一直没有深入去读,一是想自己在读lua源码之前,仅凭自己对lua使用的理解自己先实现一个简单的lua子集,二是我觉得自己实现过lua的子集之后也能帮助自己更容易的理解lua源码。前段时间,花了几个月的业余时间,实现了一个简单粗糙的lua子集(https://github.com/airtrack/luna)之后,我觉得现在可以开始读lua的源码了。

从lua.c的main函数开始,lua.c是一个stand-alone的解释器,编译完就是一个交互式命令行解释器,输入一段lua代码,然后执行并返回结果,也可以执行一个lua文件。

main:
  /* call 'pmain' in protected mode */
  lua_pushcfunction(L, &pmain);
  lua_pushinteger(L, argc);  /* 1st argument */
  lua_pushlightuserdata(L, argv); /* 2nd argument */
  status = lua_pcall(L, 2, 1, 0);
  result = lua_toboolean(L, -1);  /* get result */

main函数创建了lua_State之后就按照调用C导出给lua函数的方式调用了pmain函数。pmain函数中通过lua栈获取到命令行的argc和argv参数之后,对参数进行分析后,主要可以分为两个分支,一个处理交互命令行,一个处理文件。dotty出来交互命令行,handle_script处理lua文件。

handle_script:
  status = luaL_loadfile(L, fname);
  lua_insert(L, -(narg+1));
  if (status == LUA_OK)
    status = docall(L, narg, LUA_MULTRET);
  else
    lua_pop(L, narg);

在handle_script中先loadfile,然后docall。

loadfile会产生一个什么东西在栈上呢?写过lua的程序的人估计都会了解到下面这段lua代码:
local f = load(filename)
f()
load会将文件chunk编译成一个function,然后我们就可以对它调用。如果我们详细看lua文档的话,这个函数可以带有upvalues,也就是这个函数其实是一个闭包(closure)。按照我自己实现的那个粗糙的lua子集的方式的话,每个运行时期的可调用的lua函数都是闭包。
#define luaL_loadfile(L,f)     luaL_loadfilex(L,f,NULL)

luaL_loadfilex:
  if (filename == NULL) {
    lua_pushliteral(L, "=stdin");
    lf.f = stdin;
  }
  else {
    lua_pushfstring(L, "@%s", filename);
    lf.f = fopen(filename, "r");
    if (lf.f == NULL) return errfile(L, "open", fnameindex);
  }
  if (skipcomment(&lf, &c))  /* read initial portion */
    lf.buff[lf.n++] = '\n';  /* add line to correct line numbers */
  if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */
    lf.f = freopen(filename, "rb", lf.f);  /* reopen in binary mode */
    if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
    skipcomment(&lf, &c);  /* re-read initial portion */
  }
  if (c != EOF)
    lf.buff[lf.n++] = c;  /* 'c' is the first character of the stream */
  status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);

luaL_loadfile是一个宏,实际是luaL_loadfilex函数,在luaL_loadfilex函数中,我们发现是通过调用lua_load函数实现,lua_load的函数原型是:
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode);
定义在lapi.c中,它接受一个lua_Reader的函数并把data作为这个reader的参数。在luaL_loadfilex函数中传给lua_load作为reader是一个static函数getF,getF通过fread读取文件。

lua_load:
  ZIO z;
  int status;
  lua_lock(L);
  if (!chunkname) chunkname = "?";
  luaZ_init(L, &z, reader, data);
  status = luaD_protectedparser(L, &z, chunkname, mode);

在函数lua_load中,又将lua_Reader和data通过luaZ_init函数把数据绑定到ZIO的结构中,ZIO是buffered streams。之后调用luaD_protectedparser,此函数定义在ldo.c中,在这个函数中,我们发现它使用了构造lua_Reader和data的方式构造了调用函数f_parser和它的数据SParser,并将它们传给luaD_pcall,luaD_pcall的功能是在protected模式下用SParser数据调用f_parser函数,因此我们只需追踪f_parser函数即可。

luaD_protectedparser:
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);

f_parser:
  if (c == LUA_SIGNATURE[0]) {
    checkmode(L, p->mode, "binary");
    cl = luaU_undump(L, p->z, &p->buff, p->name);
  }
  else {
    checkmode(L, p->mode, "text");
    cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
  } 

f_parser通过数据头的signature来判断读取的数据是binary还是text的,如果是binary的数据,则调用luaU_undump来读取预编译好的lua chunks,如果是text数据,则调用luaY_parser来parse lua代码。我们发现luaU_undump和luaY_parser函数的返回值都是Closure *类型,这个刚好就和我们前面预计的一样,一个chunk load之后返回一个闭包。

进入luaY_parser函数后,就调用了一个static的mainfunc开始parse lua代码。

仔细回顾上面看过的函数,我们会发现每个C文件的导出函数都会使用lua开头,如果没有lua开头的函数都是static函数。并且我们会发现lua后的大写前缀可以标识这个函数所属的文件:
luaL_loadfile luaL_loadfilex L应该是library的意思,属于lauxlib
luaD_protectedparser luaD_pcall D是do的意思,属于ldo
luaU_undump U 是undump的意思,属于lundump
luaY_parser Y 是代表yacc的意思,lua的parser最早是用过yacc生成的,后来改成手写,名字也保留下来,属于lparser
其它的lua函数也都有这个规律
posted on 2012-07-19 18:24 airtrack 阅读(11860) 评论(3)  编辑 收藏 引用

FeedBack:
# re: lua源码剖析(一) 2012-07-19 21:14 751158936
无意中看到你的文章,感到好惭愧,我不知道该拿出什么勇气继续走下去---------爱上C++的菜鸟  回复  更多评论
  
# re: lua源码剖析(一) 2012-07-26 16:14 ynwlgh
佩服...  回复  更多评论
  
# re: lua源码剖析(一) 2014-07-24 10:02 yynote.com
分析的优点基础了,要是能深入一点就好了..  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理