运行一下程序,跟踪代码,看看这其中发生了什么?
找到 lua.c 中的 main 函数,跟进去。
首先,打开几个库,判断程序执行时是否有参数,如果无参的,也就是从命令行接受指令输入。如果有参数,则把每个参数作为脚本文件执行。为了写程序的方便,最好是采用第二种方法,就是把脚本程序写到文件中,用 Lua 解释器去执行它。命令行输入指令种方式测试个短小的程序或者是验证某个库的使用方法时还可以,但是,脚本稍微长点,就很容易引入问题:分行,语义完整都要自己去保证。所以,写脚本到文件还是可以省去很多不必要的问题的。
看一下打开库的过程中发生了什么?
先看到第一个打开库的调用是 iolib_open (); 到它的定义里去,我们看到是 7 个 lua_register 调用。
( math 是 14 个,str 是 5 个, 另外系统自带了 7 个
这一共是 33 个系统函数。这个数字是不是很熟悉,没错,就是字节码打印出来的那个。)
可以看到 lua_register 是一个宏,定义在 lua.h 中:
#define lua_register(n,f) (lua_pushcfunction(f), lua_storeglobal(n))
再分别看下 lua_pushcfunction,和 lua_storeglobal,
opcode.c 文件中
/* ** Push an object (tag=cfunction) to stack. Return 0 on success or 1 on error. */ int lua_pushcfunction (lua_CFunction fn) { if ((top-stack) >= MAXSTACK-1) { lua_error ("stack overflow"); return 1; } tag(top) = T_CFUNCTION; fvalue(top++) = fn; return 0; }
lua_pushcfunction 的参数是一个函数指针,调用结束时,栈顶的 Object 类型被置为 T_CFUNCTION, 值被设置为传入的函数指针实际参数,这里是 io_readfrom。
这里说的这个栈是 lua 和 c 相互调用的一个纽带,如同这里用 c 代码实现的一个方法,被转化成 T_CFUNCTION 类型的 Object,这种类型就是 Lua 的环境可以识别的了,于是就是了接下来的把它关联到符号表的全局符号中。
opcode.c 文件
/* ** Store top of the stack at a global variable array field. ** Return 1 on error, 0 on success. */ int lua_storeglobal (char *name) { int n = lua_findsymbol (name); if (n < 0) return 1; if (tag(top-1) == T_MARK) return 1; s_object(n) = *(--top); return 0; }
table.c 文件
/* ** Given a name, search it at symbol table and return its index. If not ** found, allocate at end of table, checking oveflow and return its index. ** On error, return -1. */ int lua_findsymbol (char *s) { struct List *l, *p; for (p=NULL, l=searchlist; l!=NULL; p=l, l=l->next) if (streq(s,l->s->name)) { if (p!=NULL) { p->next = l->next; l->next = searchlist; searchlist = l; } return (l->s-lua_table); } if (lua_ntable >= MAXSYMBOL-1) { lua_error ("symbol table overflow"); return -1; } s_name(lua_ntable) = strdup(s); if (s_name(lua_ntable) == NULL) { lua_error ("not enough memory"); return -1; } s_tag(lua_ntable) = T_NIL; p = malloc(sizeof(*p)); p->s = lua_table+lua_ntable; p->next = searchlist; searchlist = p; return lua_ntable++; }
lua_storeglobal 的作用是把全局变量存入符号表里。参数是一个字符串,就是全局变量的名字。把符号的值设置为当前栈顶的 Object,就是刚才上面说的那个栈顶的T_CFUNCTION 类型的 Object。lua_findsymbol 在符号表中查找由参数指定的字符串,如果找到,返回它在符号表中的 index, 否则,在表尾新建一个符号,返回它在表中的 index。把这个符号设置到 searchlist 的表头。
我们看下符号的定义:
typedef struct { char *name; Object object; } Symbol;
其包括一个符号的名字和一个对应的 Object 类型。
符号表里最初是有 7 个值,如下所示(table.c 文件中):
static Symbol tablebuffer[MAXSYMBOL] = { {"type",{T_CFUNCTION,{lua_type}}}, {"tonumber",{T_CFUNCTION,{lua_obj2number}}}, {"next",{T_CFUNCTION,{lua_next}}}, {"nextvar",{T_CFUNCTION,{lua_nextvar}}}, {"print",{T_CFUNCTION,{lua_print}}}, {"dofile",{T_CFUNCTION,{lua_internaldofile}}}, {"dostring",{T_CFUNCTION,{lua_internaldostring}}} }; Symbol *lua_table=tablebuffer; Word lua_ntable=7; struct List { Symbol *s; struct List *next; }; static struct List o6={ tablebuffer+6, 0}; static struct List o5={ tablebuffer+5, &o6 }; static struct List o4={ tablebuffer+4, &o5 }; static struct List o3={ tablebuffer+3, &o4 }; static struct List o2={ tablebuffer+2, &o3 }; static struct List o1={ tablebuffer+1, &o2 }; static struct List o0={ tablebuffer+0, &o1 }; static struct List *searchlist=&o0;
searchlist 是一个链表,由名字可以看出它是用来查找符号的,把上面的符号表中的各项连接起来,只在 lua_findsymbol 里使用。
每次调用 lua_findsymbol,把查找出来的结果放在该链表的头部。
lua_findsymbol 结束后,符号的名字已经在符号表中,如果是添加的符号,相应的 Object 还没有关联到符号名字上。这就是接下来 lua_storeglobal 要做的事。
lua_storeglobal 在 lua_findsymbol 返回后,利用返回的 index 找到相应的符号,设置其 Obejct 字段。
到此,方法注册 lua_register ("readfrom", io_readfrom); 调用完成。
if (tag(top-1) == T_MARK) return 1; 表示栈上没有对应的 Object,出错。
其它的库函数注册与之相同。
iolib_open (); strlib_open (); mathlib_open ();
这几个打开库的调用结束后,所有的系统函数都注册完毕。