ldo

lua の関数呼出しを受け持つのが luaD_call である。しかし、細かいエラー処理を除くと、実際には luaD_precall を実行し、 luaD_poscall しているにすぎない。そのあいだに、 luaV_execute で実際の関数のコードを呼び出しているが、これは lvm のときまで待つ。

というわけで、 luaD_precall を見てみよう。この返り値から C 関数か lua 関数かを識別しているはずである。

StkId luaD_precall (lua_State *L, StkId func) {
  LClosure *cl;
  ptrdiff_t funcr = savestack(L, func);
  if (!ttisfunction(func)) /* `func' is not a function? */
    func = tryfuncTM(L, func);  /* check the `function' tag method */
  if (L->ci + 1 == L->end_ci) luaD_growCI(L);
  else condhardstacktests(luaD_reallocCI(L, L->size_ci));
  cl = &clvalue(func)->l;
  if (!cl->isC) {  /* Lua function? prepare its call */
    /* lua 関数の処理 */
  }
  else {  /* if is a C function, call it */
    /* C 関数の処理 */
  }
}

全体の概形はこんなふうになっている。まず、関数呼出しのデータが関数型の値かどうかをチェックしている。そうでないときには tryfuncTM を呼び出している。メタテーブルで「関数呼出し」に相当するエントリがあるためである。

luaD_growCI は、 CallInfo のスタックが足りなかったときに realloc で伸ばしている操作。 condhardstacktests は気にしなくてよい。で、ようするに func のデータを取り出して、 isC かどうかを調べていると。 C 関数であれば適当な処理をして値を返す。 lua 関数なら NULL を返すはずである。

先に C 関数の処理を見てみる。

    CallInfo *ci;
    int n;
    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */
    ci = ++L->ci;  /* now `enter' new function */
    L->base = L->ci->base = restorestack(L, funcr) + 1;
    ci->top = L->top + LUA_MINSTACK;
    ci->state = CI_C;  /* a C function */
    if (L->hookmask & LUA_MASKCALL)
      luaD_callhook(L, LUA_HOOKCALL, -1);
    lua_unlock(L);
#ifdef LUA_COMPATUPVALUES
    lua_pushupvalues(L);
#endif
    n = (*clvalue(L->base - 1)->c.f)(L);  /* do the actual call */
    lua_lock(L);
    return L->top - n;

ci をひとつ進めて、新しい ci を取り出す(足りなければさっき grow_CI していたはずなのでここで足りないことはない)。で、 ci の値を初期化している。 funcr は restorestack は precall の冒頭で定義されている値で、 stack の始点と func の差を計算したもの。 restorestack はそれを再度足しているので、ようするに base を func の次のスタックとしている。で、 lua_State を引数に関数呼出しを実行しておしまい。 L->top - n というのはどういう意味かというのも後で詳しく見るが、この位置からスタックに積まれているものを、関数呼出しの返り値とするということである。つまり、 n 個の引数を持っている。

では、 lua の関数呼出しを見る。

    CallInfo *ci;
    Proto *p = cl->p;
    if (p->is_vararg)  /* varargs? */
      adjust_varargs(L, p->numparams, func+1);
    luaD_checkstack(L, p->maxstacksize);
    ci = ++L->ci;  /* now `enter' new function */
    L->base = L->ci->base = restorestack(L, funcr) + 1;
    ci->top = L->base + p->maxstacksize;
    ci->u.l.savedpc = p->code;  /* starting point */
    ci->u.l.tailcalls = 0;
    ci->state = CI_SAVEDPC;
    while (L->top < ci->top)
      setnilvalue(L->top++);
    L->top = ci->top;
    return NULL;

Proto というのは、その lua 関数のプロトタイプ情報で、引数の数であるとか、可変長引数関数かとか、最大スタックサイズとか、そういった情報を保存している。 adjust_varargs は可変長引数のための関数である。で、
maxstacksize だけスタックを伸ばしても問題ないかチェックし、CallInfo
を取り出す。こちらは maxstacksize だけ top を加えている。で、 CallInfo には lua 関数の呼出しのための機構がいくつかある。 savedpc の pc は program counter だろうから、実行するプログラムカウンタを使う。で、 NULL を返している。

adjust_varargs も見てみる。

static void adjust_varargs (lua_State *L, int nfixargs, StkId base) {
  int i;
  Table *htab;
  TObject nname;
  int actual = L->top - base;  /* actual number of arguments */
  if (actual < nfixargs) {
    luaD_checkstack(L, nfixargs - actual);
    for (; actual < nfixargs; ++actual)
      setnilvalue(L->top++);
  }
  actual -= nfixargs;  /* number of extra arguments */
  htab = luaH_new(L, actual, 1);  /* create `arg' table */
  for (i=0; i<actual; i++)  /* put extra arguments into `arg' table */
    setobj2n(luaH_setnum(L, htab, i+1), L->top - actual + i);
  /* store counter in field `n' */
  setsvalue(&nname, luaS_newliteral(L, "n"));
  setnvalue(luaH_set(L, htab, &nname), cast(lua_Number, actual));
  L->top -= actual;  /* remove extra elements from the stack */
  sethvalue(L->top, htab);
  incr_top(L);
}

nfixargs には「ふつうの引数の数」が入る。 top と base の差が実際の引数。つまり関数を呼ぶときには、まず関数オブジェクトを積み、それから引数をぜんぶ積んでから luaD_call するということになる。

ここからは、 lua のマニュアルにある次の挙動をもとに考える。

       function f(a, b) end
       function g(a, b, ...) end
       function r() return 1,2,3 end
       g(3)             a=3, b=nil, ... -->  (nothing)
       g(3, 4)          a=3, b=4,   ... -->  (nothing)
       g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8
       g(5, r())        a=5, b=1,   ... -->  2  3

まず actual が nfixargs より小さい場合。これは g(3) に対応する。このときには、通常の引数である b にも nil が入るように不足ぶんだけ top を伸ばす必要がある。

そうでない場合は actual から nfixargs を引くと、可変長引数の個数になる。が、可変長引数はテーブルとして扱う必要があるからこのままではマズい。そこで、まずテーブルを作成し、順にデータを格納していく。見直してほしいが、 luaH_new では第二引数が数値(配列)部の長さ、第三引数がハッシュの大きさなので、ほぼ配列として使っている。で、順にデータを入れていく。最後に n というキーで varargs の数を入れていた。ちょっと試す。

> function f (...) return arg.n; end
> return f()
0
> return f(1)
1
> return f(1, 2, 3)
3
> return f(1, 2, 3, 4, 5)
5

なるほどそのようになっている。

テーブルができたら、可変長引数のぶんだけ top を巻戻して、作成したテーブルをスタックに積む。