« 正式招聘 web 平台开发工程师 | 返回首页 | 开发笔记(26) : AOI 以及移动模块 »

Lua 5.2 的细节改变

最近想试一下, Lua JIT 2.0 能给我们的系统带来多大的提升。但可惜的是,我们一开始就在用 Lua 5.2 来构建系统,而 Lua JIT 2.0 只支持 Lua 5.1 的 API ,在可以看到的时间里,恐怕也不太会去支持 5.2 了。

所以,我只能想办法反向支持 Lua 5.1 。

语法层面最重大的改变是 Lua 5.2 取消了环境表这个概念,转而提供 _ENV 这个语法糖。

许多小细节是 C API 上的变化。这使得按 Lua 5.2 标准写的 C 库,无法在 Lua 5.1 环境下编译。我打算用 Lua 5.1 的 API 来模拟出来。

我并没有完全实现所有 Lua 5.2 新增的 API ,比如对 continuation function 的支持,几乎不可能在 Lua 5.1 已有的 API 上完成的。

一些简单的 API 的实现我 放在这里了

luaL_checkversion 很有用,但很难完全实现其功能。无法检测出重复链接。

lua_arith 虽然可以实现,但是直接用 5.1 的 API 模拟出来,会比较繁琐,性能低下。而我也没用它,所以就放弃了。

lua_upvalueidlua_upvaluejoin 估计也不太会用的上。

luaL_Buffer 在 Lua 5.2 里结构变了,所以,新增加的 luaL_buffinitsizeluaL_prepbuffsize 是无法实现相同的语义的,不想改变 5.1 里的 auxlib 的行为,所以只能回避用它们了。


对于 lua 层面的 API ,改变最大的是 load 的语义。这是和 _ENV 一起改变的,完美支持会比较麻烦。我先模拟了一个最简单的

一开始,我想通过修改用户传入的代码,在前面增加一小段代码来模拟 _ENV 的行为。后来发现,我们自己用的库里有一部分是用 binary 格式来在 State 间传递 function 的。而二进制模式的代码,不能通过附加文本代码的方式来改变行为。这个难题下一步再去解决。

Lua 5.2 里的 string.format 中,%s 会默认调用对象的 __tostring 方法,而 5.1 则不会。我不打算更新 5.1 中的 format 方法,还是把 lua 代码中的 format 参数显式调用一下好了。那些地方多是用来输出 log 的,只要避免将 nil 传进入就可以了。


Lua 5.2 改进了 coroutine 的支持,好在 Lua JIT 2.0 也同样支持了。但是 Lua JIT 2 似乎对 __pairs 的支持(默认关闭)是有 bug 的。今天调试了很久终于确认并非我们自己代码的 bug 。

我构造了一个简单的 test case :

local tbl = {}

local a = { values=  { foo = 1 } }

local iter = function(value)
    local function loop(t,k)
        return next(t,k)
    end
    return loop, value
end

local mt = {
    __pairs = function(self)
        local values = rawget(self, "values")
        return iter(values)
    end
}

setmetatable(a,mt)

tbl.a = a

local function trav(k,t)
    if type(t) == "table" then
        print(k, t)
        for k,v in pairs(t) do
            trav(k,v)
        end
    end
end

trav("",tbl)

在 luajit 2.0 下运行,会输出两个 a :

        table: 0x00327fa8
a       table: 0x00321d00
a       table: 0x00321d00

a 这个字段被重复迭代了两次。


今天还发现,debug 库中的 getinfo 信息,lua 5.2 比 lua 5.1 更丰富。5.2 用 nparams 字段,可以取得函数的参数个数。而 5.1 里把参数和 local 变量一视同仁。

在旧代码里,我们试图这样去取得一个函数的参数名字(为了匹配通讯协议)。

    local nparam = debug.getinfo(f,"u").nparams
    local p = {}
    for i=1,nparam do
        local name = debug.getlocal(f,i)
        table.insert(p, name)
    end

在 Lua 5.1 里,是没有 nparams 字段的。而且 getlocal 不能传入一个函数,而必须是一个堆栈层次数。我只好使用了一个变通的方案来达到相同的功能。

local function gen_hook(p)
        return function()
                local i = 1
                while true do
                        local name = debug.getlocal(2,i)
                        if name == nil or string.byte(name) == 40 then -- '(' is 40
                                break
                        end
                        p[i] = name
                        i = i + 1
                end
                error()
        end
end

function debug.params(f)
        local p = {}
        local co
        local function probe()
                debug.sethook(co, gen_hook(p), "c")
                f()
        end

        co = coroutine.create(probe)
        coroutine.resume(co)
        return p
end

Comments

lua被宿主调用后,如何实现远程中断调试,不知道有没有这方面的工具,不知道有什么好的策略。

@Siney

我笔误, 是 5.1 的 getlocal 不可以传入函数. 不是 getinfo

lua 5.1 的 getinfo函数可以传入函数,并拿到函数的信息,我这里都是这样使用的,我看lua的manual也说可以。


debug.getinfo ([thread,] function [, what])

Returns a table with information about a function. You can give the function directly, or you can give a number as the value of function, which means the function running at level function of the call stack of the given thread: level 0 is the current function (getinfo itself); level 1 is the function that called getinfo; and so on. If function is a number larger than the number of active functions, then getinfo returns nil.

The returned table can contain all the fields returned by lua_getinfo, with the string what describing which fields to fill in. The default for what is to get all information available, except the table of valid lines. If present, the option 'f' adds a field named func with the function itself. If present, the option 'L' adds a field named activelines with the table of valid lines.

For instance, the expression debug.getinfo(1,"n").name returns a table with a name for the current function, if a reasonable name can be found, and the expression debug.getinfo(print) returns a table with all available information about the print function.

我怎么调试老是出错

我怎么调试老是出错

还在用5.1.4。。不敢切换5.2,更别提jit了

lua5.1移植到luajit2的兼容性问题,有没有大大说说啊,用了luajit2有一次怪异的宕机,也没解决。

@ST 不晓得你说的LuaJIT2在生产环境不靠谱是指哪方面,从lua5.1移植到luajit2是有点儿兼容性问题必须解决,但是我们现在就是用于生产环境的。

@ST 听到这个消息,我感到万分悲痛。
@Cloud 为啥不直接用 5.1 呢

@zhouzuoji

Lua 5.2 的 _ENV 是去掉 5.1 的"环境"这个语言核心特性, 用一个语法糖来兼容.

这是在做减法.

上面想说的是弊大于利:)

云风一直在倒腾不同的语言工具不同的版本,感觉完全没有必要。选择最适合自己的一个稳定版本就行了。现在很多互联网服务器跑的还是古老的unix和linux发行版呢。引入太多语法糖比多写几行代码,显然利大于弊。

LuaJIT在生产环境感觉很不靠谱. 之前尝试把项目转到LuaJIT, 发现了几处崩溃. 尽力解决掉一部分, 但后来经过评估, 还是没敢上.

Post a comment

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