lua-lua源码笔记.md

小记一度。表示曾经也看过源码的人。



实例化luaState

luastate初始化相关功能,如元表、词法解析等

1
2
3
4
5
6
7
8
9
10
11
12
13
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
UNUSED(ud);
stack_init(L, L); /* init stack */
init_registry(L, g);
luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ //初始化短字符表容量
luaT_init(L); //元表
luaX_init(L); //词法解析器
/* pre-create memory-error message */
g->memerrmsg = luaS_newliteral(L, MEMERRMSG);
luaS_fix(g->memerrmsg); /* it should never be collected */
g->gcrunning = 1; /* allow gc */
}

表相关


内存分配器

  • 内存分配器是一个 分配内存释放内存 的方法,在 lauxlib.c 中的 l_alloc 方法中,通过函数指针的形式绑定在全局状态机 global_State 中的 f*realloc *中

    1
    2
    3
    4
    5
    LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
    ...
    g->frealloc = f;
    ...
    }
  • 所有分配内存的方法都通过 g->frealloc 来分配内存,如下

    1
    newblock = (*g->frealloc)(g->ud, block, osize, nsize);
  • 个性定制。可以在这个分配内存的方法中,做一些内存大小占用的的统计。云风的 AOI 库中就有个实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    static void * my_alloc(void * ud, void *ptr, size_t sz) {
    struct alloc_cookie * cookie = ud;
    if (ptr == NULL) {
    void *p = malloc(sz);
    ++ cookie->count;
    cookie->current += sz;
    if (cookie->max < cookie->current) {
    cookie->max = cookie->current;
    }
    // printf("%p + %u\n",p, sz);
    return p;
    }
    -- cookie->count;
    cookie->current -= sz;
    // printf("%p - %u \n",ptr, sz);
    free(ptr);
    return NULL;
    }

对象生成

都是通过类似的方法生成一个 GCObject 对象,然后对具体要使用的具体对象拿出来赋值,如 cluv

1
2
Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl;
UpVal *uv = &luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal), NULL, 0)->uv;
  • GCObject 数据结构,里面包含 所有类型,及gc公共头。在c++面向对象编程中,则是用继承的方式,实现gc公共头。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    union GCObject {
    GCheader gch; /* common header */
    union TString ts;
    union Udata u;
    union Closure cl;
    struct Table h;
    struct Proto p;
    struct UpVal uv;
    struct lua_State th; /* thread */
    };

全局状态机


短字符

  • 定义长度小于等于40的为短字符,在 luaconf.h#define LUAI_MAXSHORTLEN 40

  • 全部都放在 global_Statestringtable

  • 生成判定。lstring.c 中的 luaS_newlstr 方法中

    1
    2
    if (l <= LUAI_MAXSHORTLEN)  /* short string? */
    return internshrstr(L, str, l);

    lstring.c 中的 internshrstr 方法中,先去全局状态机中找,如果找不到就 生成短字符

  • 生成短字符 的方法是 lstring.c 中的 newshrstr 方法。

    • 如果已使用的数量 >= 短字符哈希容器的容量(数组部分),则调整哈希容器的容量
      1
      2
      if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
      luaS_resize(L, tb->size*2); /* too crowded */

长字符

  • 生成一个对象。lstring.c 中的 createstrobj 方法

元表相关

new luaState 的时候把 17 个元表方法名(短字符)挂在了 global_State 中的 tmname
ltm.c 中的 luaT_init 方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void luaT_init (lua_State *L) {
static const char *const luaT_eventname[] = { /* ORDER TM */
"__index", "__newindex",
"__gc", "__mode", "__len", "__eq",
"__add", "__sub", "__mul", "__div", "__mod",
"__pow", "__unm", "__lt", "__le",
"__concat", "__call"
};
int i;
for (i=0; i<TM_N; i++) {
G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
luaS_fix(G(L)->tmname[i]); /* never collect these names */
}
}

函数闭包相关

  • 函数的原型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    typedef struct Proto {
    CommonHeader;
    TValue *k; /* constants used by the function */
    Instruction *code;
    struct Proto **p; /* functions defined inside the function */
    int *lineinfo; /* map from opcodes to source lines (debug information) */
    LocVar *locvars; /* information about local variables (debug information) */
    Upvaldesc *upvalues; /* upvalue information */
    union Closure *cache; /* last created closure with this prototype */
    TString *source; /* used for debug information */
    int sizeupvalues; /* size of 'upvalues' */
    int sizek; /* size of `k' */
    int sizecode;
    int sizelineinfo;
    int sizep; /* size of `p' */
    int sizelocvars;
    int linedefined;
    int lastlinedefined;
    GCObject *gclist;
    lu_byte numparams; /* number of fixed parameters */
    lu_byte is_vararg;
    lu_byte maxstacksize; /* maximum stack used by this function */
    } Proto;
  • 闭包缓存。在函数原型 Proto 中有个 cache 专门用来缓存 闭包A 里面的 闭包B。每次往闭 包A 中压入 闭包B 时,都会先重缓存取,没有才会新建并缓存。在 lvm.c 中:

    1
    2
    3
    4
    5
    Closure *ncl = getcached(p, cl->upvals, base);  /* cached closure */
    if (ncl == NULL) /* no match? */
    pushclosure(L, p, cl->upvals, base, ra); /* create a new one */
    else
    setclLvalue(L, ra, ncl); /* push cashed closure */

    pushclosure 方法中缓存 闭包B 到 闭包A中

    1
    p->cache = ncl;  /* save it on cache for reuse */
  • 寻找upvalue。lfunc.c 中的 luaF_findupval 方法。

    • 没有找到则新生产一个uv,链入全局状态机 global_State 中的 UpVal uvhead; 双链表的表头

语法解析相关

define NUM_RESERVED


GC相关

  • 在内存分配 ==失败== 时,会进行一次 全局的gc,在 lmem.cluaM_realloc 方法中的
    luaC_fullgc(L, 1); /* try to free some memory... */

其他

  • lua_locklua_unlock
    1
    2
    #define lua_lock(L)     ((void) 0)
    #define lua_unlock(L) ((void) 0)
    这种宏到底几个意思?原来是个开发者做扩展是,用于重写,加入线程锁。
    • 当需要将 Lua 移植到其他平台时,可以重写 lua_lock。必须注意的是,为了避免线程间对同一 Lua 对象的操作,lua_lock 的定义必须是互斥的,且其实现中其行为应该和 Python 的全局解释器锁(GIL)类似。
    • 详情可参考 Purpose of lua_lock and lua_unlock?