Lua學習與交流——Lua函數調用過程(轉載)

【本文內容轉自:http://blog.chinaunix.net/uid-52437-id-2108797.html

第3章 lua中函數調用的方法

         前面,以及分析了lua中定義一個函數的方法,現在總結如下:

1、  將函數名作爲局部變量存在局部變量表裏,並在棧上開闢一個寄存器空間,在運行期,將新建一個closure,並存在已保留的寄存器裏;

2、  將在語法解析階段新建的FuncState結構體保存在其父函數的局部函數定義數組裏。

        

         現在,就要分析,當lua進行函數調用的時候,是怎麼調用的呢?

        

         當分析到這裏,對於lua生成中間碼的過程就比較熟悉了。關鍵是生成的中間碼必須要和lua虛擬機的執行聯繫在一起。所以,對於這裏分析的函數調用,要結合lua虛擬機的執行一起來分析。

        

         上篇文章對生成局部函數中間碼做了簡單的介紹。這裏知道,當lua發現一個新定義的函數的時候,會生成OP_CLOSURE指令。那麼,lua虛擬機執行到OP_CLOSURE後怎麼執行呢?

         在此之前,先說在lua解析代碼完了以後,會做那些善後之事呢?

         前面說過,lua會把一個代碼文件當作是一個函數解析執行。在解析期間,它會率先生成一個FuncState的結構,作爲最外面的函數。但這是解析時做的事情,運行期間,是不會有FuncState這個東西出現的。在運行期間,是由一個個叫CallInfo的數據結果的,它指的是當前運行的函數。

         那麼,在解析代碼以後,是怎麼轉入運行的呢?

        

         祕密就在lua做的一些善後工作。在f_parser()函數裏,解析完了後,最外層的那個FuncState是有的,其實用的是FuncState裏的函數頭Proto。然後,lua會新建一個閉包(Closure),通過函數luaF_newLclosure(),並把剛纔解析代碼生成的Proto保存在這個閉包裏,cl->l.p=tf,新建完了這個Closure,lua會把它壓入棧。然後就轉入運行期進行運行。也就是進入lua_pcall()函數執行。而lua_pcall()這個函數就是假設在當前棧頂有一個Closure,然後執行這個Closure。具體說來如下:

         lua_pcall()經過一系列調用,最後通過調用luaD_precall()來執行。

         這個luaD_precall()做什麼呢?如果這個函數是C函數,那麼就直接執行,如果是lua函數,這個函數便字如其人了,就是做執行前的準備。

         首先,這個函數就會在L的ci數組裏申請一項空槽。也就是說,L裏有一個CallInfo數組,代表每個執行的函數。然後用現在的Closure初始化這個CallInfo。並且將這個函數的棧底標誌在棧頂,也就是那個Closure壓入棧的地方。

         接着,就是轉入lua的虛擬機的運行了。也就是函數luaV_execute()。

 

         這時,如果遇到OP_CALL的時候,也就是在lua代碼中有函數調用的時候,就會直接用luaD_precall(),將要執行的函數加載到L->ci中去,然後重新開始luaV_execute()虛擬機執行。

        

         與調用函數相對應的,怎麼從一個函數裏返回呢?

 

         也就是,當lua虛擬機遇到OP_RETUREN的時候,該如何操作呢?

 

         lua會通過一個函數來實現從子函數到調用它的函數裏的一個轉變:luaD_poscall()。在這個函數裏,lua主要做的事情就是,因爲L->ci是個鏈,代表當前運行函數的嵌套調用鏈,調用過的函數會被直接丟棄。於是,只要L->ci--,也就是讓當前ci指針退一格,指向前面調用它的函數,即可返回。另外,要將棧底位置和pc值恢復,這些恢復都很簡單。

 

         但是,最主要的一件事,就是如果這個函數裏含有upvalue怎麼辦?這個留在明天再研究一下。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章