很抱歉,這周實在是太忙了。。。不廢話,直接進入主題。css
所謂的分析思路,無非就是找到程序的入口點,按執行流程一步一步地閱讀分析,或者從本身感興趣的部分入手。這個系列暫時還不是對Lua的全面剖析,只是選取其中一部分來分析。所以咱們要找到本身感興趣的部分,有針對性地進行分析。html
因爲Lua的官方文檔比較齊全,瀏覽一下官方提供的資料集會比較便於咱們着手分析。算法
所謂的源碼分析,就是從源代碼構建可執行程序,調試調試,看看執行流程,看看主要的數據結構和算法、程序的運行狀態之類的,最後再品味一下設計(問題背景、緣由、優缺點),僅此而已。編程
那咱們先來看看源代碼的佈局。api
能夠從Makefile文件入手,不過這裏用的是Windows + Visual Studio,能夠省去這個步驟。
看看從官方下載的源碼包,提供瞭如下文件:數據結構
./lua-5.3.0/ Makefile README /doc/ contents.html logo.gif lua.1 lua.css luac.1 manual.css manual.html osi-certified-72x60.png readme.html /src/ lapi.c lapi.h lauxlib.c lauxlib.h lbaselib.c lbitlib.c lcode.c lcode.h lcorolib.c lctype.c lctype.h ldblib.c ldebug.c ldebug.h ldo.c ldo.h ldump.c lfunc.c lfunc.h lgc.c lgc.h linit.c liolib.c llex.c llex.h llimits.h lmathlib.c lmem.c lmem.h loadlib.c lobject.c lobject.h lopcodes.c lopcodes.h loslib.c lparser.c lparser.h lprefix.h lstate.c lstate.h lstring.c lstring.h lstrlib.c ltable.c ltable.h ltablib.c ltm.c ltm.h lua.c lua.h lua.hpp luac.c luaconf.h lualib.h lundump.c lundump.h lutf8lib.c lvm.c lvm.h lzio.c lzio.h Makefile
第一件事固然是看README啦還用說,固然專業的作法能夠看Makefile,這裏爲了不引入其餘無關知識,仍是選用簡單的方法。閉包
按照說明查看doc/readme.html文件其中的【Building Lua on other systems】節,咱們發現它介紹了lua可執行程序大體的組成和依賴關係以下:數據結構和算法
library: lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c lutf8lib.c loadlib.c linit.c interpreter: library, lua.c compiler: library, luac.c
根據這個簡單的依賴關係,我用VS2015創建了相應的工程,便於調試。其實官方資料集裏也提供了現成的VS工程的下載連接:編輯器
TODO:此處應有下載連接^_^函數
由於咱們分析的重點是編譯原理和虛擬機的部分,而不是相關的庫的實現部分。所以應該從 lua.c 或 luac.c 開始入手,其實咱們從文件列表中也能夠看出,裏面有幾個比較重要的文件:
llex.c lopcodes.c lparser.c lvm.c
到底從哪裏入手比較好,就見仁見智了。這裏我仍是採用了官方提供的資料來幫助選擇。
官方的資料集的wiki中提供了一個頁面。該頁面介紹了這些文件的用途、編程約定、模塊結構等等。
爲了不這個頁面失效,下面仍是可恥地複製粘貼略帶翻譯地提供給你們,網上也有一些翻譯,只是他們翻譯時省略掉了一些我以爲有用的信息。個別簡單的,我就不翻譯了。
ldebug.c
- 調試接口。包括如下功能:
訪問調試鉤子 (lua_sethook, lua_gethook, lua_gethookcount),
訪問運行時棧信息 (lua_getstack / lua_getlocal / lua_setlocal),
檢查字節碼 (luaG_checkopenop / luaG_checkcode),
引起錯誤 (luaG_typeerror / luaG_concaterror / luaG_aritherror /luaG_ordererror / luaG_errormsg / luaG_runerror)
lzio.c
- 一種緩衝輸入流接口。
lmem.c
- 內存管理接口。它實現了這些內存分配函數:luaM_realloc / luaM_growaux_
lgc.c
- 增量式GC (內存管理)
lstate.c
- 全局狀態。包括:
用於打開和關閉Lua states的函數(lua_newstate/lua_close)
線程相關函數 (luaE_newthread / luaE_freethread)
lobject.c
- 一些針對Lua對象的通用函數。包括:
數據類型與其字符串形式的互相轉換
原始數據相等性測試(luaO_rawequalObj)
日誌基礎設施2(luaO_log2)
lstring.c
- string table (持有由Lua處理的全部字符串)
lfunc.c
- 用來操縱原型和閉包的輔助函數。
ltable.c
- Lua tables (hash)
lcode.c
- Lua的代碼生成器. 由 lparser.c 來使用
llex.c
- 詞法分析器. 由 lparser.c 來使用
lparser.c
- Lua 解析器.
lundump.c
- 加載通過預編譯的Lua代碼塊:
實現了用於加載預編譯後的代碼塊的luaU_undump 函數。
還提供了用來解析函數頭的 luaU_header 函數(在luaU_undump()內部被調用)。
ldump.c
- 用於保存通過預編譯的Lua代碼塊:
實現了用於轉儲函數對象的luaU_dump()函數。這種函數對象是從文件或字符串預編譯而來的Lua代碼塊。
lopcodes.c
- 由Lua虛擬機使用的操做碼。
經過 luaP_opnames 和 luaP_opmodes這兩個映射表,定義了全部操做碼的名稱和相關信息。
lvm.c
- Lua虛擬機:
用於執行字節碼 (luaV_execute).
還暴露了少許函數供 lapi.c 使用(如:luaV_concat).
ldo.c
- Lua函數調用和棧管理。處理函數調用 (luaD_call / luaD_pcall), 棧生長, 協程處理等
ltm.c
- 標籤方法(tag methods)。 實現了查詢對象中的元方法的功能。
lbaselib.c - (base functions) lstrlib.c - string ltablib.c - table lmathlib.c - math loslib.c - os liolib.c - io loadlib.c - package ldblib.c - debug
lapi.c
- Lua API. Lua C API的主要實現部分(lua_* functions).
lauxlib.c
- 定義了 luaL_* 函數
linit.c
- 實現了 luaL_openlibs 用於從C語言環境中加載上述模塊。
lua.c
- 獨立的Lua 解析器
print.c
- 定義了 "PrintFunction?" 函數,這些函數用於打印字節碼 (經過luac.c "-l" 選項來使用)
luac.c
- Lua 編譯器 (保存字節碼到文件/列出字節碼)
搞清楚Lua的模塊結構後,咱們須要着重分析的模塊就出來了:
lua 和 luac 程序的實現模塊
處理執行Lua字節碼的模塊
語法分析與代碼生成相關模塊
基本數據類型的實現模塊
固然,到了後期,若是有時間,我還會再分析一下Lua的垃圾收集器,但最近實在是太忙太忙了。
如下是對 lua-5.3.0/src/Makefile 文件重要部分的節選
LUA_A= liblua.a CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ ltm.o lundump.o lvm.o lzio.o LIB_O= lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o \ lmathlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o loadlib.o linit.o BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS) LUA_T= lua LUA_O= lua.o LUAC_T= luac LUAC_O= luac.o
根據咱們感興趣的模塊,大概是如下目標文件:
lua.o
luac.o
llex.o
ldo.o
lstate.o
lparser.o
lopcodes.o
lvm.o
lcode.o
ldump.o
lundump.o
ltm.o
lobject.o
lstring.o
ltable.o
lfunc.o
(不得不吐槽這Markdown編輯器的Bug,莫名其妙給其餘其餘條目自動加粗了。。。我本來都不是這麼寫的!!!!)
其實仍是挺多的,不過比較重要的都用粗體標出來了。大體就是按這個順序去分析。這些其實也能夠直接看源文件中引用的頭文件,但那樣太麻煩了,而且,Lua源碼有時候並非在文件的頂部寫include指令,而是在中間的某個地方,比較蛋疼。看Makefile是比較方便並且專業的作法。咱們繼續看一下Makefile 文件中上述目標文件依賴關係:
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lobject.h llimits.h \ lstate.h ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldo.h \ lobject.h lstate.h ltm.h lzio.h lmem.h lgc.h llex.h lparser.h lstring.h \ ltable.h ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ lparser.h lstring.h ltable.h lundump.h lvm.h lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \ lstring.h ltable.h lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ ldo.h lfunc.h lstring.h lgc.h ltable.h lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \ ltable.h lvm.h lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ ldo.h lgc.h lstring.h ltable.h lvm.h
這裏簡要地說一下Makefile語法:冒號左邊的.o是編譯時生成的目標文件,冒號右邊是生成這個目標文件須要的源文件,\ 表示還沒寫完,下一行繼續寫。上一段Makefile代碼中,等號左邊是一個變量,等號右邊是這個變量的值。
關於Makefile:Makefile是GNU/Linux系統中用於自動化構建的DSL,供gnu make使用,和Android開發中的.gradle文件做用相似。不過VS和Qt也都使用了各自的Makefile格式和工具。VS有nmake,Qt有qmake,跨平臺的有cmake,這裏就不展開說了,本身去看文檔吧。
如今,咱們能夠比較有針對性地去看源文件了。
下一期將介紹一下Lua源碼中的編程約定(其實仍是作個搬運工+翻譯工,這裏的翻譯就湊合着看吧,翻譯質量應該沒有太大問題)。前面這幾期都是比較無聊又不可或缺的,只能忍忍啦。預報一下,後天將更新第三彈。