修改Lua源码实现Lua只读表

图片还没有哦

首先找到元表枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef enum {
TM_INDEX,
TM_NEWINDEX,
TM_ASSIGN,
TM_GC,
TM_MODE,
TM_LEN,
TM_EQ, /* last tag method with `fast' access */
TM_ADD,
TM_SUB,
TM_MUL,
TM_DIV,
TM_MOD,
TM_POW,
TM_UNM,
TM_LT,
TM_LE,
TM_CONCAT,
TM_CALL,
TM_N /* number of elements in the enum */
} TMS;

在里面添加一个元素 TM_ASSIGN

再找到void 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","__assign"
"__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 */
}
}

在里面添加__assign

最后找到void luaV_settable函数

下面这段源码有些函数作用是猜想的 可能并不是很准确 lua5.2.4的函数 可能版本之间有着小差异 但是只要理解了 我想应该都不影响

lua 5.2.4源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void luaV_settable(lua_State *L, const TValue *t, TValue *key, StkId val)
{
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++)
{
const TValue *tm;
if (ttistable(t)) //判断表是否存在
{
Table *h = hvalue(t); //获取表中的值
TValue *oldval = cast(TValue *, luaH_get(h, key)); //获取旧的值

//ttisnil 是否为false [ ((!ttisnil) 表示当值存在的时候 或者(||) 当 没有__nexindex方法的时候) 并且(&&) 当没有相同值的时候 或者(||) 有新的键值的时候 进入 条件 进行表中的赋值修改操作]
if (!ttisnil(oldval) || ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && (oldval != luaO_nilobject || (oldval = luaH_newkey(L, h, key) , 1) )))
{
//当有这个TM__ASSIGN元方法存在(不能对已有的键值 进行修改)
if (((tm = fasttm(L, h->metatable, TM_ASSIGN))))
{
//当值不存在的时候才阔以写入
if (ttisnil(oldval))
{
setobj2t(L, oldval, val);
invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val);
return;
}
}
else
{
setobj2t(L, oldval, val);
invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val);
return;
}
}
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) //不是一个表 检查元方法
luaG_typeerror(L, t, "index");

if (ttisfunction(tm)) //如果元方法是一个函数 这个应该就是函数回调
{
callTM(L, tm, t, key, val, 0);
return;
}
t = tm;
}
luaG_runerror(L, "loop in settable");
}

经过上面的操作 __assign 可以这样使用

1
2
3
4
5
6
7
8
tab = { --只读元表
__assign = function(t,k,v) --对已有的key值不能进行操作
error('read only __assign:' .. tostring(k).. '__'.. tostring(v))
end,
__newindex = function(t,k,v) --不能进行表的写入
error('read only __newindex:' .. tostring(k)..'__'.. tostring(v))
end,
}

如果这个表中 新插入的键值是已有的 就会走到这

lua 5.1.5源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void luaV_settable(lua_State *L, const TValue *t, TValue *key, StkId val)
{
int loop;
TValue temp;
for (loop = 0; loop < MAXTAGLOOP; loop++)
{
const TValue *tm = NULL;
if (ttistable(t))
{
Table *h = hvalue(t);
TValue *oldval = luaH_set(L, h, key);
//当旧值为空 TM_NEWINDEX 为空的时候 把元素添加进原来的表
if (ttisnil(oldval) && (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{
setobj2t(L, oldval, val);
h->flags = 0;
luaC_barriert(L, h, val);
return;
}
//当这个值存在 并且 要没有TM_ASSIGN元方法才阔以添加 如果添加不进去
else if (!ttisnil(oldval) && (tm = fasttm(L, h->metatable, TM_ASSIGN)) == NULL)
{
setobj2t(L, oldval, val);
h->flags = 0;
luaC_barriert(L, h, val);
return;
}
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");

if (ttisfunction(tm))
{
callTM(L, tm, t, key, val);
return;
}
/* else repeat with `tm' */
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
t = &temp;
}

luaG_runerror(L, "loop in settable");
}

MAXTAGLOOP 如果用元方法继承 层次操作这个值 就会失效 有必要的时候阔以自己调整 (这个是方法链限制 避免循环)

本文标题:修改Lua源码实现Lua只读表

文章作者:游戏人生

发布时间:2019年02月21日 - 18:02

最后更新:2020年12月27日 - 12:12

原始链接:http://www.tjl-myblog.cn/修改lua源码实现lua只读表.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------