http://eric-weitm.iteye.com/blog/1500082
lua 源代码解析
源代码版本:lua-5.2.0
一、概览
1、lua脚本中暴露的库相关
lbaselib.c - 基础函数库(方便使用lua内部的功能)
lbitlib.c - 位运算库
lcorolib.c - 协程库
lstrlib.c - 字符串库,提供通用字符串处理功能
lstring.c - 字符串表(保存所有由Lua操作的字符串集合)
lmathlib.c - 数学库
loslib.c - 操作系统相关库,时间、环境、语言、执行程序等
liolib.c - 输入输出库
loadlib.c - 模块库(实现require函数,package函数)
ldblib.c - 调试库
ldebug.c - 调试接口:包含访问调试钩子的函数(lua_sethook/lua_gethook/lua_gethookcount),访问运行时堆栈信息的函数(lua_getstatck/lua_getlocal/lua_setlocal),检查字节码函数(luaG_checkopenop/luaG_checkcode),和抛出错误的函数(luaG_typeerror/luaG_concaterror/luaG_aritherror/luaG_ordererror/luaG_errormsg/luaG_runerror)
lapi.c - Lua的API.实现Lua C API(lua_*函数)集合
lauxlib.c - 定义所有的luaL_*函数集
2、lua runtime的数据结构相关
lfunc.c - 包装原型和闭包的辅助函数
ltable.c - Lua表实现(哈希)
lobject.c - 一些针对Lua对象的通用函数。包括数据类型到字符串转换函数,纯数据相等测试函数(luaO_rawequalObj),和日志基础2(luaO_log2)
ltm.c - 标记原语方法。实现对象访问原语方法(metathods)
3、编译前端
llex.c - 词法分析器,被lparser.c使用,接口LUAI_FUNC void luaX_next (LexState *ls);
lparser.c - Lua语法检查器,接口 Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar);
lundump.c - 加载预编译后的Lua代码块,函数luaU_undump,加载一个预编译后的代码块;luaU_header(被luaU_undump使用的内部函数),用来检查函数头部。
ldump.c - 保存预编译后的Lua代码块。函数luaU_dump,使用预编译后的代码字符串展示一个函数;
lcode.c - Lua的代码生成器。被lparser.c使用,static int luaK_code (FuncState *fs, Instruction i) 用于写入一条指令
顺序:lex--parse--code--dump
4、虚拟机、运行时相关
lopcodes.c - Lua虚拟机的操作码。定义所有操作符的名称和信息(使用luaP_opnames和luaP_opmodes两个表保存)
lvm.c - Lua虚拟机。执行字节码(luaV_execute)。也暴漏一些lapi.c可能用到的函数(例如:luaV_concat)
ldo.c - Lua的栈和调用结构。控制函数调用(luaD_call/luaD_pcall),栈增长,协同代码的同步
lstate.c - 全局上下文信息即 符号表(函数表、变量表、线程等). 包括打开和关闭LUA上线文的函数(lua_newstate/lua_close)和lua线程函数(luaE_newthread/luaE_freethread)
lmem.c - 内存管理的接口.通过封装内存分配函数,实现了luaM_realloc / luaM_growaux_两个函数.
lgc.c - 垃圾回收器(内存管理)
5、外部封装
lua.c - Lua独立解释器
luac.c - Lua编译器(保存字节码到一个文件,也可以列出字节码)
6、其他
lzio.c - 一个通用的带缓冲区的输入流接口
linit.c - 实现luaL_openlibs方法,便于在C语言中加载上述模块
lctype.c 判断类型
二、主流程
第一阶段:main---load----词法、语法解析、parser内会生成需要的指令集
第二阶段:docall----虚拟机内的luaV_execute
三、runtime的实现
1、vc中的一个典型调用栈信息如下:
> lua1.exe!luaV_execute(lua_State * L=0x00035c28) Line 521 C
lua1.exe!luaD_call(lua_State * L=0x00035c28, lua_TValue * func=0x00035e00, int nResults=-1, int allowyield=0) Line 393 + 0x9 bytes C
lua1.exe!f_call(lua_State * L=0x00035c28, void * ud=0x0013f4a0) Line 920 + 0x18 bytes C
lua1.exe!luaD_rawrunprotected(lua_State * L=0x00035c28, void (lua_State *, void *)* f=0x00424a60, void * ud=0x0013f4a0) Line 133 + 0x1f bytes C
lua1.exe!luaD_pcall(lua_State * L=0x00035c28, void (lua_State *, void *)* func=0x00424a60, void * u=0x0013f4a0, int old_top=40, int ef=32) Line 591 + 0x11 bytes C
lua1.exe!lua_pcallk(lua_State * L=0x00035c28, int nargs=0, int nresults=-1, int errfunc=3, int ctx=0, int (lua_State *)* k=0x00000000) Line 946 + 0x20 bytes C
lua1.exe!docall(lua_State * L=0x00035c28, int narg=0, int nres=-1) Line 179 + 0x19 bytes C
lua1.exe!handle_script(lua_State * L=0x00035c28, char * * argv=0x00035ba0, int n=1) Line 341 + 0xf bytes C
lua1.exe!pmain(lua_State * L=0x00035c28) Line 469 + 0x17 bytes C
lua1.exe!luaD_precall(lua_State * L=0x00035c28, lua_TValue * func=0x00035de0, int nresults=1) Line 317 + 0x9 bytes C
lua1.exe!luaD_call(lua_State * L=0x00035c28, lua_TValue * func=0x00035de0, int nResults=1, int allowyield=0) Line 392 + 0x11 bytes C
lua1.exe!f_call(lua_State * L=0x00035c28, void * ud=0x0013fe48) Line 920 + 0x18 bytes C
lua1.exe!luaD_rawrunprotected(lua_State * L=0x00035c28, void (lua_State *, void *)* f=0x00424a60, void * ud=0x0013fe48) Line 133 + 0x1f bytes C
lua1.exe!luaD_pcall(lua_State * L=0x00035c28, void (lua_State *, void *)* func=0x00424a60, void * u=0x0013fe48, int old_top=8, int ef=0) Line 591 + 0x11 bytes C
lua1.exe!lua_pcallk(lua_State * L=0x00035c28, int nargs=2, int nresults=1, int errfunc=0, int ctx=0, int (lua_State *)* k=0x00000000) Line 946 + 0x20 bytes C
lua1.exe!main(int argc=2, char * * argv=0x00035ba0) Line 495 + 0x13 bytes C
2、基本交互逻辑
vm循环取出一个一个的Instruction,之后一个巨大的switch来解释这个Instruction(lua 5.2是基于寄存器的指令集),即经典的fetch---execute循环。
特殊的指令:
1》函数调用指令:遇到函数调用时(lua中调用),要用c代码模拟出硬件执行代码时的栈结构。
基本上每次增加一个活动记录时要设置好相应的闭包信息和执行环境,设置好相应的符号表,做好参数的检查,必要时GC一下
2》协程指令 :每次有create调用时就新建一个lua_state,全局栈维护这些stack信息
resume时 luaB_coresume---auxresume---lua_resume---luaB_yield
lua1.exe!lua_yieldk(lua_State * L=0x0003e698, int nresults=1, int ctx=0, int (lua_State *)* k=0x00000000) Line 556 C
lua1.exe!luaB_yield(lua_State * L=0x0003e698) Line 99 + 0x1a bytes C
lua1.exe!luaD_precall(lua_State * L=0x0003e698, lua_TValue * func=0x0003f3f8, int nresults=-1) Line 317 + 0x9 bytes C
lua1.exe!luaV_execute(lua_State * L=0x0003e698) Line 744 + 0x34 bytes C
lua1.exe!resume(lua_State * L=0x0003e698, void * ud=0x0003f3d8) Line 496 + 0x9 bytes C
lua1.exe!luaD_rawrunprotected(lua_State * L=0x0003e698, void (lua_State *, void *)* f=0x004348d0, void * ud=0x0003f3d8) Line 133 + 0x1f bytes C
lua1.exe!lua_resume(lua_State * L=0x0003e698, lua_State * from=0x00035c28, int nargs=2) Line 531 + 0x1d bytes C
> lua1.exe!auxresume(lua_State * L=0x00035c28, lua_State * co=0x0003e698, int narg=2) Line 31 + 0x11 bytes C
lua1.exe!luaB_coresume(lua_State * L=0x00035c28) Line 53 + 0x1d bytes C
四、解释器
1、main函数
line484: lua.c
lua_newstate:全局和线程栈状态的初始化
lua_State *L = luaL_newstate(); // 创建全局的运行时信息
lua_pushcfunction(L, &pmain); // 创建当前栈顶的函数闭包
lua_pushinteger(L, argc); // 参数入栈
lua_pushlightuserdata(L, argv); /* 2nd argument */
status = lua_pcall(L, 2, 1, 0); // 运行时执行一个lua函数
result = lua_toboolean(L, -1); /* get result */
finalreport(L, status);
lua_close(L); // 释放内存等扫尾工作
2、lua_pcall详情
1》参数检查
2》luaD_pcall --------luaD_rawrunprotected---f_call----luaD_call---luaD_precall---pmain
luaL_checkversion(L); // 验证虚拟机
lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
luaL_openlibs(L); // 初始化内置库 loadedlibs是默认打开的功能表
lua_gc(L, LUA_GCRESTART, 0);
handle_script -------执行主脚本
luaL_loadfile------docall(一堆的栈操作)----luaV_execute:lvm.c // 虚拟机的执行引擎
lua 源代码解析
最新推荐文章于 2023-03-01 15:43:59 发布