LuaJIT虛擬機-函數與原型

Lua中的函數(或Function),其實應該是閉包(Closure),閉包能夠認爲是函數+外部變量,這裏爲了簡單沒有做區分,函數原型(或Proto)能夠認爲是函數的靜態表示。數組

函數與函數原型的關係有點相似系統中進程與程序,一個程序被屢次啓動會建立多個進程。一個Proto能夠被此加載建立多個函數。Proto是靜態的,Function是動態的,在Lua中調用一個函數,老是先依照Proto建立一個函數對象,再執行這個函數對象。閉包

函數原型 Proto

函數原型表明了一個函數定義,是解釋器編譯lua代碼獲得的,包括一系列lua指令、常量等的集合。函數

GCProto結構體表明瞭一個函數的原型,結構體的組成以下所示。佈局

typedef struct GCproto {
  GCHeader;
  uint8_t numparams;	/* Number of parameters. */
  uint8_t framesize;	/* Fixed frame size. */
  MSize sizebc;		/* Number of bytecode instructions. */
#if LJ_GC64
  uint32_t unused_gc64;
#endif
  GCRef gclist;
  MRef k;		/* Split constant array (points to the middle). */
  MRef uv;		/* Upvalue list. local slot|0x8000 or parent uv idx. */
  MSize sizekgc;	/* Number of collectable constants. */
  MSize sizekn;		/* Number of lua_Number constants. */
  MSize sizept;		/* Total size including colocated arrays. */
  uint8_t sizeuv;	/* Number of upvalues. */
  uint8_t flags;	/* Miscellaneous flags (see below). */
  uint16_t trace;	/* Anchor for chain of root traces. */
  /* ------ The following fields are for debugging/tracebacks only ------ */
  GCRef chunkname;	/* Name of the chunk this function was defined in. */
  BCLine firstline;	/* First line of the function definition. */
  BCLine numline;	/* Number of lines for the function definition. */
  MRef lineinfo;	/* Compressed map from bytecode ins. to source line. */
  MRef uvinfo;		/* Upvalue names. */
  MRef varinfo;		/* Names and compressed extents of local variables. */
} GCproto;

GCProto結構體主要成員ui

  • GCHeader: GC類型對象的通用頭部
  • k: 指向常量數組中,GC類型的常量和數值類型常量的分割點,GC型常量索引位負,數值型常量索引位正。
  • sizekgc: GC類型常量的數量
  • sizekn: 數值型常量的數量
  • uv: 指向UpValue列表
  • sizeuv: UpValue的數量
  • sizebc: 字節碼指令的數量。指令存放在GCProto結構體後面

函數原型的內存佈局以下圖所示this

Proto結構示意圖

函數 Function

LuaJIT中函數(Function)的定義以下lua

/* -- Function object (closures) ------------------------------------------ */

/* Common header for functions. env should be at same offset in GCudata. */
#define GCfuncHeader \
  GCHeader; uint8_t ffid; uint8_t nupvalues; \
  GCRef env; GCRef gclist; MRef pc

typedef struct GCfuncC {
  GCfuncHeader;
  lua_CFunction f;	/* C function to be called. */
  TValue upvalue[1];	/* Array of upvalues (TValue). */
} GCfuncC;

typedef struct GCfuncL {
  GCfuncHeader;
  GCRef uvptr[1];	/* Array of _pointers_ to upvalue objects (GCupval). */
} GCfuncL;

typedef union GCfunc {
  GCfuncC c;
  GCfuncL l;
} GCfunc;

GCfunc是一個union,封裝了Lua函數和C函數,這裏只關注Lua函數GCfuncL,GCfuncHeader是通用的函數頭部,其中.net

  • uint8_t nupvalues表示upvalue的個數
  • Mref pc 是一個指針,指向相應的GCProto中字節碼指令部分

GCfuncL還有另外一個成員,GCfunc upptr[1]指向upvalue對象的數組。debug

函數的操做數

函數中操做的數據由三種指針

  • 常量,保存在函數對應的原型中,經過KSTR/KNIL等指令獲取
  • 全局變量,保存在函數的環境中,經過GSET/GGET等指令獲取/更新,可參考Lua中的全局變量與環境
  • upvalue,表明函數中用到的外部變量。
  • 函數參數,調用時傳入

如下面的代碼爲例

local code = 2
local function output()
    local a = 「code:" .. tostring(code) 
    print(a)
end

對於函數output來講,

  • code是upvalue,由於是在函數外層定義的
  • "a"是局部變量
  • 2"code:"是常量,會保存在Proto中
  • print是全局變量(若是引用了一個變量,而這兩變量不是局部變量,如a,也不是在函數外層定義的,如code,編譯器便會把他看成全局變量),

建立函數對象

依照Proto建立一個函數對象有兩種狀況

加載一個Lua腳本時建立函數

加載一個Lua腳本時,首先會調用lj_parse解析腳本生成一個Proto結構體,而後調用lj_func_newL_empty建立一個Lua函數而後執行,經過解析Lua腳本生成的函數是非嵌套函數,是沒有upvalue的。

/* Create a new Lua function with empty upvalues. */
GCfunc *lj_func_newL_empty(lua_State *L, GCproto *pt, GCtab *env)
{
  GCfunc *fn = func_newL(L, pt, env);
  MSize i, nuv = pt->sizeuv;
  /* NOBARRIER: The GCfunc is new (marked white). */
  for (i = 0; i < nuv; i++) {
    GCupval *uv = func_emptyuv(L);
    int32_t v = proto_uv(pt)[i];
    uv->immutable = ((v / PROTO_UV_IMMUTABLE) & 1);
    uv->dhash = (uint32_t)(uintptr_t)pt ^ (v << 24);
    setgcref(fn->l.uvptr[i], obj2gco(uv));
  }
  fn->l.nupvalues = (uint8_t)nuv;
  return fn;
}

FNEW字節碼指令建立

對於在Lua腳本中定義的函數,加載時只會生成相應的Proto,建立函數對象的操做隱藏在字節碼指令中,Lua解釋器會在函數調用以前插入一個FNEW的指令,這條指令會依照Proto建立相應的函數對象。

相應的邏輯在lj_func_new_gc中,與前一種方式最大的不一樣在於須要加載Upvalue,也須要繼承父函數的環境。

/* Do a GC check and create a new Lua function with inherited upvalues. */
GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
{
  GCfunc *fn;
  GCRef *puv;
  MSize i, nuv;
  TValue *base;
  lj_gc_check_fixtop(L);
  fn = func_newL(L, pt, tabref(parent->env));
  /* NOBARRIER: The GCfunc is new (marked white). */
  puv = parent->uvptr;
  nuv = pt->sizeuv;
  base = L->base;
  for (i = 0; i < nuv; i++) {
    uint32_t v = proto_uv(pt)[i];
    GCupval *uv;
    if ((v & PROTO_UV_LOCAL)) {
      uv = func_finduv(L, base + (v & 0xff));
      uv->immutable = ((v / PROTO_UV_IMMUTABLE) & 1);
      uv->dhash = (uint32_t)(uintptr_t)mref(parent->pc, char) ^ (v << 24);
    } else {
      uv = &gcref(puv[v])->uv;
    }
    setgcref(fn->l.uvptr[i], obj2gco(uv));
  }
  fn->l.nupvalues = (uint8_t)nuv;
  return fn;
}
相關文章
相關標籤/搜索