修改 Lua

lua-users home
wiki

以下提供了一些修改 Lua 源代码以改变 Lua 功能的第一步。下面的示例不一定是修改的最佳选择,也不是进行这些修改的最佳方法。但是,它们被选中是因为它们的影响以及被修改的代码的性质。本教程是为 Lua 5.1.1 编写的。

任务

这在下面得到了最好的说明。我们希望修改 Lua,以便以下代码按所示运行。

test1.lua

function configmodified(t,k,v)
	print("Config Modified, call reload")
	rawset(t,k,v)
end

config = { maxthreads = 10, minthreads = 2 }
setmetatable(config, {__setindex = configmodified})

以及结果

D:\code\lua\mod\bin>lua -i test1.lua
Lua 5.1.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
table: 00940180
> =config.minthreads
2
> config.minthreads = 3
Config Modified, call reload
> =config.minthreads
3
> config.sleepdelay = 100
Config Modified, call reload

修改

内部代码将元方法称为“标签方法”,基本列表位于 ltm.h 中

/*
* WARNING: if you change the order of this enumeration,
* grep "ORDER TM"
*/
typedef enum {
  TM_INDEX,
  TM_NEWINDEX,
  TM_GC,
  TM_MODE,
  TM_EQ,  /* last tag method with `fast' access */
  TM_ADD,
  TM_SUB,
  TM_MUL,
  TM_DIV,
  TM_MOD,
  TM_POW,
  TM_UNM,
  TM_LEN,
  TM_LT,
  TM_LE,
  TM_CONCAT,
  TM_CALL,
  TM_N		/* number of elements in the enum */
} TMS;

TM_EQ 的注释非常重要。由于设置值需要高性能,我们需要看看这一点。

“快速”访问的工作方式是在每个表上存储一个标志,并标记标志是否存在。

表在 lobject.h 中定义

typedef struct Table {
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 
  lu_byte lsizenode;  /* log2 of size of `node' array */
  struct Table *metatable;
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  GCObject *gclist;
  int sizearray;  /* size of `array' array */
} Table;

Table.flags 是 1 字节,由于每个标志占用 1 位,我们可以最多有 8 个“快速访问”元方法。基本 Lua 带有 5 个设置为快速访问,为我们提供了 3 个空间。我们将使用其中一个用于 SETINDEX。因此

ltm.h - mods


typedef enum {
  TM_INDEX,
  TM_NEWINDEX,
  TM_SETINDEX,	// AA - new metatag that is called everytime a value is set, its use disables TM_NEWINDEX'''
  TM_GC,
  TM_MODE,
  TM_EQ,  /* last tag method with `fast' access */
  ...

我们还需要给这个新标签一个名称,它位于

ltm.c - luaT_init

  static const char *const luaT_eventname[] = {  /* ORDER TM */
    "__index", "__newindex", 
    "__gc", "__mode", "__eq",
    "__add", "__sub", "__mul", "__div", "__mod",
    "__pow", "__unm", "__len", "__lt", "__le",
    "__concat", "__call"
  };

我们将将其更改为

  static const char *const luaT_eventname[] = {  /* ORDER TM */
    "__index", "__newindex", "__setindex",   // AA - Added name for setindex
    "__gc", "__mode", "__eq",
    ...

最后,我们需要使这段代码工作。大部分繁重的工作发生在 lvm.c 中。设置值(不是 rawset)的标准调用通过 luaV_settable 进行。让我们看看

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)) {  /* `t' is a table? */
      Table *h = hvalue(t);
      TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
      if (!ttisnil(oldval) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
        setobj2t(L, oldval, val);
        luaC_barriert(L, h, val);
        return;
      }
      /* else will try the tag method */
    }
    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;
    }
    t = tm;  /* else repeat with `tm' */ 
  }
  luaG_runerror(L, "loop in settable");
}

我们将这样修改它。


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)) {  /* `t' is a table? */
      Table *h = hvalue(t);

      TValue *oldval;	// AA -- Have to declare this here

	  // AA - Our new code here
	  tm = fasttm(L, h->metatable, TM_SETINDEX);
	  if(tm != NULL)  {
		if (ttisfunction(tm)) {
		  callTM(L, tm, t, key, val);
		  return;
		}
	  }

      oldval = luaH_set(L, h, key); /* do a primitive set */
      if (!ttisnil(oldval) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
        setobj2t(L, oldval, val);
        luaC_barriert(L, h, val);
        return;
      }
      ...


现在我们编译并测试。顶部的代码现在应该按预期工作。

评论

这应该是对 Lua 的一个非常基本但有效的第一个修改。我建议做的另一件事是更改 Version 和 Release 常量,以便很容易发现这是一个修改后的 Lua 版本。它们位于 lua.h 中

不要更改 LUA_VERSION 或 LUA_RELEASE 中的数字!附加一个字符串,例如 '(Kirk3)'。 --lhf

其中一个问题是每次设置都需要额外的测试。可以通过以下设计考虑来缓解这种情况。

---

有一些论文描述了 Lua 的实现[1]。另请参阅 Lua 5.1 VM 指令的简明介绍[2]。Yueliang[3](Lua 中的 Lua 实现)也可能有所帮助。

除了直接修改 Lua 代码,还可以考虑使用 MetaLua[4]LuaTokenParsing 等工具。鉴于 Lua 强大的元编程能力(特别是 代码生成),你甚至可能不需要修改 Lua 本身。


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2017 年 10 月 21 日下午 12:15 GMT (差异)