剖析一下LuaJIT的Table
Opened this issue · 0 comments
bingoohuang commented
剖析一下LuaJIT的Table
安装LuaJIT
因为是在openresty里面使用LuaJIT的,那么就直接从openresty/luajit2下载一份源代码吧。
看到有Makefile文件,直接make报错,
~/Downloads/luajit2-2.1-agentzh
🕙[ 21:19:27 ] ❯ make
==== Building LuaJIT 2.1.0-beta3 ====
/Library/Developer/CommandLineTools/usr/bin/make -C src
Makefile:323: *** missing: export MACOSX_DEPLOYMENT_TARGET=XX.YY. Stop.
make: *** [default] Error 2
谷歌大法,找到MacOS源码安装LuaJIT
$ export MACOSX_DEPLOYMENT_TARGET=10.14
$ make install PREFIX=~/luajit
然后设置一下PATH环境变量,就可以看到luajit装好了
🕙[ 22:41:36 ] ❯ which luajit
/Users/bingoobjca/luajit/bin/luajit
🕙[ 22:41:59 ] ❯ luajit -v
LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2020 Mike Pall. https://luajit.org/
hack源代码,增加打印table信息
在src/lj_strfmt.c中,修正函数lj_strfmt_obj,增加对tostring的处置
/* Raw conversion of object to string. */
GCstr * LJ_FASTCALL lj_strfmt_obj(lua_State *L, cTValue *o)
{
if (tvisstr(o)) {
return strV(o);
} else if (tvisnumber(o)) {
return lj_strfmt_number(L, o);
} else if (tvisnil(o)) {
return lj_str_newlit(L, "nil");
} else if (tvisfalse(o)) {
return lj_str_newlit(L, "false");
} else if (tvistrue(o)) {
return lj_str_newlit(L, "true");
} else {
char buf[8+2+2+16], *p = buf;
p = lj_buf_wmem(p, lj_typename(o), (MSize)strlen(lj_typename(o)));
*p++ = ':'; *p++ = ' ';
if (tvisfunc(o) && isffunc(funcV(o))) {
p = lj_buf_wmem(p, "builtin#", 8);
p = lj_strfmt_wint(p, funcV(o)->c.ffid);
} else if (tvistab(o)) { // 从这里开始是新增的代码
p = lj_strfmt_wptr(p, lj_obj_ptr(G(L), o));
*p++ = '\0';
printf("-- %s\n", buf);
GCtab *t = tabV(o);
/* print array part */ {
uint32_t i, asize = t->asize;
printf("-- a[%d]: ", asize);
TValue *array = tvref(t->array);
for (i = 0; i < asize; i++) {
if (i>0) printf(", ");
cTValue *o = &array[i];
if (tvisstr(o)) printf("%s", strdata(strV(o)));
else if (tvisnum(o)) printf("%g", numV(o));
else if (tvisnil(o)) printf("nil");
else printf("?");
}
printf("\n");
}
/* print hashmap part */ {
uint32_t i, hmask = t->hmask;
printf("-- h[%d]: ", hmask+1);
Node *node = noderef(t->node);
for (i = 0; i <= hmask; i++) {
if (i>0) printf(", ");
Node *n = &node[i];
cTValue *k = &n->key;
cTValue *v = &n->val;
if (tvisstr(k)) printf("%s", strdata(strV(k)));
else if (tvisnum(k)) printf("%g", numV(k));
else if (tvisnil(k)) printf("nil");
else printf("?");
printf("=");
if (tvisstr(v)) printf("%s", strdata(strV(v)));
else if (tvisnum(v)) printf("%g", numV(v));
else if (tvisnil(v)) printf("nil");
else printf("?");
}
printf("\n");
}
// 从这里新增的代码结束
} else {
p = lj_strfmt_wptr(p, lj_obj_ptr(G(L), o));
}
return lj_str_new(L, buf, (size_t)(p - buf));
}
}
剖析table
habr.lua
jit.off()
ffi = require('ffi')
require('table.new')
require('table.clear')
local function caption(h, c)
print(h)
print(string.rep(c or '-', #h))
print()
end
function traverse(fn, t)
local str = '-- '
for k, v, n in (fn or pairs)(t) do
if str ~= '-- ' then
str = str .. ', '
end
str = string.format('%s%s=%s%s',
str, k, v, n and ', ' or ''
)
end
print(str)
end
local function codeblock(str)
print('```lua')
for l in string.gmatch(str, "([^\n]*)\n") do
print(l)
assert(loadstring(l))()
end
print('```\n')
end
caption('Welcome to the LuaJIT internals', '=')
--------------------------
caption('Анатомия таблиц')
codeblock([[
t = {}
tostring(t)
]])
codeblock([[
t["a"] = "A"
t["b"] = "B"
t["c"] = "C"
tostring(t)
]])
codeblock([[
t1 = {a = 1, b = 2, c = 3}
tostring(t1)
t2 = {c = 3, b = 2, a = 1}
tostring(t2)
traverse(pairs, t1)
traverse(pairs, t2)
]])
codeblock([[
t2["c"] = nil
traverse(pairs, t2)
tostring(t2)
print('-- ', next(t2, "c"))
]])
------------------
caption('Массивы')
codeblock([[
t = {1, 2}
tostring(t)
]])
codeblock([[
t = {[2] = 2, 1}
tostring(t)
]])
---------------------------
caption('Итератор pairs()')
codeblock([[
t = table.new(4, 4)
for i = 1, 8 do t[i] = i end
tostring(t)
traverse(pairs, t)
]])
codeblock([[
t[9] = 9
tostring(t)
]])
-------------------------------------
caption('Длина таблицы table.getn()')
codeblock([[
print(#{nil, 2})
print(#{[2] = 2})
]])
codeblock([[
tostring({nil, 2})
tostring({[2] = 2})
]])
----------------------------------
caption('Сортировка table.sort()')
----------------------------------
caption('Pack / unpack')
codeblock([[
t = {nil, 2}
tostring(t)
traverse(pairs, t)
print(unpack(t, 1, t.n))
]])
----------------------------
caption('Итератор ipairs()')
codeblock([[
t = {1, 2, nil, 4}
print(#t) -- UB
traverse(ipairs, t)
]])
------------------------------
caption('Подводные камни FFI')
codeblock([[
NULL = ffi.new('void*', nil)
print(type(NULL))
print(type(nil))
print(NULL == nil)
if NULL then print('NULL is not nil') end
]])
os.exit(1)
运行
🕙[ 22:46:35 ] ❯ luajit habr.lua
Welcome to the LuaJIT internals
===============================
Анатомия таблиц
t = {}
tostring(t)
-- table: 0x07cd7c80
-- a[0]:
-- h[1]: nil=nil
t["a"] = "A"
t["b"] = "B"
t["c"] = "C"
tostring(t)
-- table: 0x07cd7c80
-- a[0]:
-- h[4]: nil=nil, b=B, a=A, c=C
t1 = {a = 1, b = 2, c = 3}
tostring(t1)
-- table: 0x07cd8890
-- a[0]:
-- h[4]: nil=nil, b=2, a=1, c=3
t2 = {c = 3, b = 2, a = 1}
tostring(t2)
-- table: 0x07cd8f08
-- a[0]:
-- h[4]: nil=nil, b=2, c=3, a=1
traverse(pairs, t1)
-- b=2, a=1, c=3
traverse(pairs, t2)
-- b=2, c=3, a=1
t2["c"] = nil
traverse(pairs, t2)
-- b=2, a=1
tostring(t2)
-- table: 0x07cd8f08
-- a[0]:
-- h[4]: nil=nil, b=2, c=nil, a=1
print('-- ', next(t2, "c"))
-- a 1
Массивы
t = {1, 2}
tostring(t)
-- table: 0x07cda5d8
-- a[3]: nil, 1, 2
-- h[1]: nil=nil
t = {[2] = 2, 1}
tostring(t)
-- table: 0x07cdaa28
-- a[2]: nil, 1
-- h[2]: nil=nil, 2=2
Итератор pairs()
t = table.new(4, 4)
for i = 1, 8 do t[i] = i end
tostring(t)
-- table: 0x07cdaec0
-- a[5]: nil, 1, 2, 3, 4
-- h[4]: 7=7, 8=8, 5=5, 6=6
traverse(pairs, t)
-- 1=1, 2=2, 3=3, 4=4, 7=7, 8=8, 5=5, 6=6
t[9] = 9
tostring(t)
-- table: 0x07cdaec0
-- a[17]: nil, 1, 2, 3, 4, 5, 6, 7, 8, 9, nil, nil, nil, nil, nil, nil, nil
-- h[1]: nil=nil
Длина таблицы table.getn()
print(#{nil, 2})
2
print(#{[2] = 2})
0
tostring({nil, 2})
-- table: 0x07cdc660
-- a[3]: nil, nil, 2
-- h[1]: nil=nil
tostring({[2] = 2})
-- table: 0x07cdc848
-- a[0]:
-- h[2]: nil=nil, 2=2
Сортировка table.sort()
Pack / unpack
t = {nil, 2}
tostring(t)
-- table: 0x07cdcdd0
-- a[3]: nil, nil, 2
-- h[1]: nil=nil
traverse(pairs, t)
-- 2=2
print(unpack(t, 1, t.n))
nil 2
Итератор ipairs()
t = {1, 2, nil, 4}
print(#t) -- UB
4
traverse(ipairs, t)
-- 1=1, 2=2
Подводные камни FFI
NULL = ffi.new('void*', nil)
print(type(NULL))
cdata
print(type(nil))
nil
print(NULL == nil)
true
if NULL then print('NULL is not nil') end
NULL is not nil