lua-lua源码笔记.md
小记一度。表示曾经也看过源码的人。
- 参考资料
- 云风的 Lua 源码欣赏
- lua5.2.2的vs工程
- 编译lua参考
实例化luaState
luastate初始化相关功能,如元表、词法解析等
1 | static void f_luaopen (lua_State *L, void *ud) { |
表相关
- 图文并茂 : http://blog.csdn.net/zr339361504/article/details/52432163
- 获取table数组部分的长度,不是用* table.getn* 而是用 rawlen
内存分配器
内存分配器是一个 分配内存 和 释放内存 的方法,在 lauxlib.c 中的
l_alloc
方法中,通过函数指针的形式绑定在全局状态机 global_State 中的 f*realloc *中1
2
3
4
5LUA_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
18static 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 对象,然后对具体要使用的具体对象拿出来赋值,如 cl 和 uv
1 | Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl; |
- GCObject 数据结构,里面包含 所有类型,及gc公共头。在c++面向对象编程中,则是用继承的方式,实现gc公共头。
1
2
3
4
5
6
7
8
9
10union 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_State
的stringtable
中生成判定。lstring.c 中的
luaS_newlstr
方法中1
2if (l <= LUAI_MAXSHORTLEN) /* short string? */
return internshrstr(L, str, l);lstring.c 中的
internshrstr
方法中,先去全局状态机中找,如果找不到就 生成短字符。生成短字符 的方法是 lstring.c 中的
newshrstr
方法。- 如果已使用的数量 >= 短字符哈希容器的容量(数组部分),则调整哈希容器的容量
1
2if (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 | void luaT_init (lua_State *L) { |
函数闭包相关
函数的原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23typedef 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
5Closure *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;
双链表的表头
- 没有找到则新生产一个uv,链入全局状态机
语法解析相关
define NUM_RESERVED
GC相关
- 在内存分配 ==失败== 时,会进行一次 全局的gc,在 lmem.c 的
luaM_realloc
方法中的
luaC_fullgc(L, 1); /* try to free some memory... */
其他
- lua_lock 和 lua_unlock 这种宏到底几个意思?原来是个开发者做扩展是,用于重写,加入线程锁。
1
2- 当需要将 Lua 移植到其他平台时,可以重写 lua_lock。必须注意的是,为了避免线程间对同一 Lua 对象的操作,lua_lock 的定义必须是互斥的,且其实现中其行为应该和 Python 的全局解释器锁(GIL)类似。
- 详情可参考 Purpose of lua_lock and lua_unlock?