« 星际争霸2编辑器的初接触 | 返回首页 | 近期攀岩小记 »

让 LuaJIT 2.0 支持 Lua 5.2 中的 _ENV 特性

我们的项目是用 Lua 5.2 标准来写的, 最近想迁移到 LuaJIT 2.0 中。其中碰到的最大障碍是,LuaJIT 2.0 不支持 Lua 5.2 中的 _ENV 特性。而且,看起来将来也不会支持了。

在邮件列表中,LuaJIT 的作者 Mike 看起来很不喜欢这个新特性

可是我真的需要它,所以只好自己阅读 luajit 的源代码,给它打了个 patch 支持这个特性。

patch (基于 luajit 2.0 的 beta 11) 如下:

diff --git a/src/lib_base.c b/src/lib_base.c
index 568216e..b58a5a4 100644
--- a/src/lib_base.c
+++ b/src/lib_base.c
@@ -363,6 +363,10 @@ static int load_aux(lua_State *L, int status, int envarg)
       GCtab *t = tabV(L->base+envarg-1);
       setgcref(fn->c.env, obj2gco(t));
       lj_gc_objbarrier(L, fn, t);
+      if (LJ_52) {
+        lua_pushvalue(L, envarg);
+        lua_setupvalue(L, -2, 1);
+     }
     }
     return 1;
   } else {
diff --git a/src/lj_lex.c b/src/lj_lex.c
index b54d2a2..59b2b84 100644
--- a/src/lj_lex.c
+++ b/src/lj_lex.c
@@ -381,6 +381,7 @@ int lj_lex_setup(lua_State *L, LexState *ls)
   ls->lookahead = TK_eof;  /* No look-ahead token. */
   ls->linenumber = 1;
   ls->lastline = 1;
+  ls->env = NULL;
   lj_str_resizebuf(ls->L, &ls->sb, LJ_MIN_SBUF);
   next(ls);  /* Read-ahead first char. */
   if (ls->current == 0xef && ls->n >= 2 && char2int(ls->p[0]) == 0xbb &&
diff --git a/src/lj_lex.h b/src/lj_lex.h
index d16461a..37c15eb 100644
--- a/src/lj_lex.h
+++ b/src/lj_lex.h
@@ -73,6 +73,7 @@ typedef struct LexState {
   BCInsLine *bcstack;  /* Stack for bytecode instructions/line numbers. */
   MSize sizebcstack;   /* Size of bytecode stack. */
   uint32_t level;  /* Syntactical nesting level. */
+  GCstr *env;  /* const _ENV */
 } LexState;

 LJ_FUNC int lj_lex_setup(lua_State *L, LexState *ls);
diff --git a/src/lj_load.c b/src/lj_load.c
index e30421e..5a389af 100644
--- a/src/lj_load.c
+++ b/src/lj_load.c
@@ -40,6 +40,9 @@ static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud)
   }
   pt = bc ? lj_bcread(ls) : lj_parse(ls);
   fn = lj_func_newL_empty(L, pt, tabref(L->env));
+  if (LJ_52) {
+    settabV(L, uvval(&gcref(fn->l.uvptr[0])->uv), tabref(L->env)); /* Set env table to upvalue 1 */
+  }
   /* Don't combine above/below into one statement. */
   setfuncV(L, L->top++, fn);
   return NULL;
diff --git a/src/lj_parse.c b/src/lj_parse.c
index 29def7b..2f435a5 100644
--- a/src/lj_parse.c
+++ b/src/lj_parse.c
@@ -1112,6 +1112,12 @@ static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first)
    fscope_uvmark(fs, reg);  /* Scope now has an upvalue. */
       return (MSize)(e->u.s.aux = (uint32_t)fs->varmap[reg]);
     } else {
+      if (LJ_52 && name == fs->ls->env && fs->prev == NULL) {
+        fscope_uvmark(fs,0);
+        expr_init(e, VUPVAL, 0);
+        e->u.s.aux = 0;
+        return 0;
+      }
       MSize vidx = var_lookup_(fs->prev, name, e, 0);  /* Var in outer func? */
       if ((int32_t)vidx >= 0) {  /* Yes, make it an upvalue here. */
    e->u.s.info = (uint8_t)var_lookup_uv(fs, vidx, e);
@@ -1128,7 +1134,9 @@ static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first)

 /* Lookup variable name. */
 #define var_lookup(ls, e) \
-  var_lookup_((ls)->fs, lex_str(ls), (e), 1)
+  var_lookup_((ls)->fs, lex_str(ls), (e), 1); if (LJ_52 && (e)->k == VGLOBAL) var_global_((ls), (e))
+
+static void var_global_(LexState *ls, ExpDesc *e);

 /* -- Goto an label handling ---------------------------------------------- */

@@ -1687,6 +1695,18 @@ static void expr_index(FuncState *fs, ExpDesc *t, ExpDesc *e)
   t->u.s.aux = expr_toanyreg(fs, e);  /* 0..255: register */
 }

+/* Convert global to _ENV index. */
+static void var_global_(LexState *ls, ExpDesc *e)
+{
+  FuncState *fs = ls->fs;
+  ExpDesc key;
+  expr_init(&key, VKSTR, 0);
+  key.u.sval = e->u.sval;
+  var_lookup_(fs, ls->env, e, 1);
+  expr_toanyreg(fs, e);
+  expr_index(fs, e, &key);
+}
+
 /* Parse index expression with named field. */
 static void expr_field(LexState *ls, ExpDesc *v)
 {
@@ -2732,6 +2752,16 @@ GCproto *lj_parse(LexState *ls)
   fs.bcbase = NULL;
   fs.bclim = 0;
   fs.flags |= PROTO_VARARG;  /* Main chunk is always a vararg func. */
+
+  if (LJ_52) {
+    /* Create upvalue named _ENV */
+    ls->env = lj_parse_keepstr(ls, "_ENV", 4);  /* Anchor _ENV string, 4 is sizeof _ENV */ 
+    var_new(ls, 0, ls->env);
+    fs.uvmap[0] = 0;
+    fs.uvtmp[0] = 0;
+    fs.nuv = 1;
+  }
+
   fscope_begin(&fs, &bl, 0);
   bcemit_AD(&fs, BC_FUNCV, 0, 0);  /* Placeholder. */
   lj_lex_next(ls);  /* Read-ahead first token. */

Comments

你这patch没对bc分支创建env啊,这样会崩的

我认为 _ENV 是 lua 5.2 的一个重大改进. 因为 _ENV 只是一个语法糖, 利用它简化了语言. 把全局表和环境这两个概念从语言中去掉了.

它的确在 jit 下, 降低了全局表的访问速度. 因为jit 为了优化, 是把全局表访问作为单独的 bytecode 来实现的.

但我认为这个影响对我的项目没有什么影响.我只是在 parser 里把所有 VGLOBAL 改成了 VINDEXED 对名为 _ENV 的 upvalue 访问而已.

而且我相信, 大部分 lua 程序并不常访问全局表.

Mike说这会影响全局变量的访问速度,而且没什么明显的好处,也许确实不容易优化,所以才说need bigger changes.

奇怪,这个patch很小啊,为啥Mike说That would need bigger changes to the VM,会不会影响其它?

风风,你真他妈的牛逼哄哄。。。。

会不会影响jit其他功能?

真的很厉害。。。

真厉害。。。

Post a comment

非这个主题相关的留言请到:留言本