関数

lua では関数もファーストクラス値だった。型は次のようになっている。

#define ClosureHeader \
        CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist

typedef struct CClosure {
  ClosureHeader;
  lua_CFunction f;
  TObject upvalue[1];
} CClosure;


typedef struct LClosure {
  ClosureHeader;
  struct Proto *p;
  TObject g;  /* global table for this closure */
  UpVal *upvals[1];
} LClosure;


typedef union Closure {
  CClosure c;
  LClosure l;
} Closure;

クロージャの実体は CClosure と LClosure 。 CClosure は C の関数呼出しを抽象化したもので、 LClosure は Lua の関数定義を抽象化している。 UpVal というのは、そのクロージャの外の環境に属するがグローバルな環境ではないところにある変数を意味している。

ClosureHeader では、 isC が「Cの関数かどうか」をチェックするフラグになっている。

Lua の関数の細かな情報は UpVal で定義されている。

typedef struct Proto {
  CommonHeader;
  TObject *k;  /* constants used by the function */
  Instruction *code;
  struct Proto **p;  /* functions defined inside the function */
  int *lineinfo;  /* map from opcodes to source lines */
  struct LocVar *locvars;  /* information about local variables */
  TString **upvalues;  /* upvalue names */
  TString  *source;
  int sizeupvalues;
  int sizek;  /* size of `k' */
  int sizecode;
  int sizelineinfo;
  int sizep;  /* size of `p' */
  int sizelocvars;
  int lineDefined;
  GCObject *gclist;
  lu_byte nups;  /* number of upvalues */
  lu_byte numparams;
  lu_byte is_vararg;
  lu_byte maxstacksize;
} Proto;


typedef struct LocVar {
  TString *varname;
  int startpc;  /* first point where variable is active */
  int endpc;    /* first point where variable is dead */
} LocVar;

このようになっている。だいたいコメントを読めばわかるので略。

クロージャはたぶん、関数実行のときに細かく参照されるのではないかと思う。 lfunc では主に初期化等しかないので、それほど面白くない。面白いのは UpVal を探索する findupval だが、ここは実際の呼出し元がわからないと何をやっているのかわからない気がするのであとまわし。