luapy (9) closure and upvalue

1) closure

class Closure:
    def __init__(self, proto, py_func, n):
        self.proto = proto
        self.py_func = py_func
        self.upvals = []
        if proto and len(proto.upvalues) > 0:
            self.upvals = [None] * len(proto.upvalues)
        elif py_func and n > 0:
            self.upvals = [None] * n

2) lua stack

from consts import Consts


class LuaStack:
    MAX_STACK_SIZE = 1000

    def __init__(self, lua_state):
        self.slots = []
        self.closure = None
        self.varargs = None
        self.pc = 0
        self.caller = None
        self.lua_state = lua_state
        self.open_upvalues = {}

    # ...

    def abs_index(self, idx):
        if idx <= Consts.LUA_REGISTRYINDEX:
            return idx

        return idx if idx >= 0 else idx + len(self.slots) + 1

    def is_valid(self, idx):
        # upvalues
        if idx < Consts.LUA_REGISTRYINDEX:
            uvidx = Consts.LUA_REGISTRYINDEX - idx - 1
            return self.closure is not None and uvidx < len(self.closure.upvals)

        if idx == Consts.LUA_REGISTRYINDEX:
            return True

        idx = self.abs_index(idx)
        return (idx > 0) and (idx <= len(self.slots))

    def get(self, idx):
        # upvalues
        if idx < Consts.LUA_REGISTRYINDEX:
            uvidx = Consts.LUA_REGISTRYINDEX - idx - 1
            if self.closure and len(self.closure.upvals) > uvidx:
                return self.closure.upvals[uvidx]
            return None

        if idx == Consts.LUA_REGISTRYINDEX:
            return self.lua_state.registry

        if not self.is_valid(idx):
            return None
        return self.slots[self.abs_index(idx)-1]

    def set(self, idx, val):
        # upvalues
        if idx < Consts.LUA_REGISTRYINDEX:
            uvidx = Consts.LUA_REGISTRYINDEX - idx - 1
            if self.closure and len(self.closure.upvals) > uvidx:
                self.closure.upvals[uvidx] = val
            return

        if idx == Consts.LUA_REGISTRYINDEX:
            self.lua_state.registry = val
            return

        if not self.is_valid(idx):
            raise Exception('Invalid Index')
        self.slots[self.abs_index(idx)-1] = val
    
    # ...

3) lua state

class LuaState:
    def __init__(self):
        self.stack = LuaStack(self)
        self.registry = LuaTable(0, 0)
        self.registry.put(Consts.LUA_RIDX_GLOBALS, LuaTable(0, 0))
        self.push_lua_stack(LuaStack(Consts.LUA_MIN_STACK))

    # ...
    
    def load(self, chunk):
        bc = BinaryChunk(chunk)
        proto = bc.undump()
        closure = Closure(proto, None, 0)
        self.stack.push(closure)

        if len(proto.upvalues) > 0:
            env = self.registry.get(Consts.LUA_RIDX_GLOBALS)
            closure.upvals[0] = env
            print('env: ', end='')
            env.dump()
        return ThreadStatus.OK

    def load_proto(self, idx):
        proto = self.stack.closure.proto.get_protos()[idx]
        c = Closure(proto, None, 0)
        self.stack.push(c)

        for i in range(len(proto.upvalues)):
            upvalue = proto.get_upvalues()[i]
            idx = upvalue.get_idx()
            if upvalue.get_in_stack():
                if idx not in self.stack.open_upvalues:
                    self.stack.open_upvalues[idx] = self.stack.slots[idx]
                c.upvals[i] = self.stack.open_upvalues[idx]
            else:
                c.upvals[i] = self.stack.closure.upvals[idx]

    def push_py_function(self, func):
        py_closure = Closure(None, func, 0)
        self.stack.push(py_closure)

    def push_py_closure(self, py_func, n):
        closure = Closure(None, py_func, n)
        for i in range(n, 0, -1):
            v = self.stack.pop()
            closure.upvals[i-1] = v
        self.stack.push(closure)

    def close_upvalues(self, a):
        for k, v in self.stack.open_upvalues:
            if k >= a-1:
                v.migrate()
                self.stack.open_upvalues.pop(k)
    
    # ...

4) instruction

def jmp(inst, vm):
    a, sbx = inst.a_sbx()
    vm.add_pc(sbx)
    if a != 0:
        vm.close_upvalues(a)


def lua_upvalue_index(idx):
    return Consts.LUA_REGISTRYINDEX - idx


def getupval(inst, vm):
    a, b, _ = inst.a_b_c()
    a += 1
    b += 1
    vm.copy(lua_upvalue_index(b), a)


def setupval(inst, vm):
    a, b, _ = inst.a_b_c()
    a += 1
    b += 1
    vm.copy(a, lua_upvalue_index(b))


def gettabup(inst, vm):
    a, b, c = inst.a_b_c()
    a += 1
    b += 1

    vm.get_rk(c)
    vm.get_table(lua_upvalue_index(b))
    vm.replace(a)


def settabup(inst, vm):
    a, b, c = inst.a_b_c()
    a += 1

    vm.get_rk(b)
    vm.get_rk(c)
    vm.set_table(lua_upvalue_index(a))

5) test

function newCounter ()
    local count = 0
    return function () -- 匿名函数
        count = count + 1
        return count
    end
end

c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
from lua_state import LuaState


def py_print(ls):
    nargs = ls.get_top()
    for i in range(1, nargs+1):
        if ls.is_boolean(i):
            print('%t', ls.to_boolean(i), end='')
        elif ls.is_string(i):
            print(ls.to_string(i), end='')
        else:
            print(ls.type_name(ls.type(i)), end='')

        if i < nargs:
            print('\t', end='')

    print()
    return 0


def main():
    with open('./test/closure_upvalue.luac', 'rb') as f:
        data = f.read()
        ls = LuaState()
        ls.register('print', py_print)
        ls.load(data)
        ls.call(0, 0)


if __name__ == '__main__':
    main()

6) result

env: {'print': <closure.Closure object at 0x7f70a7afe780>}
[01] CLOSURE      [function][nil]
[02] SETTABUP     [function][nil]
[03] GETTABUP     [function][nil]
[01] LOADK        [0][nil]
[02] CLOSURE      [0][function]
[03] RETURN       [0][function][function]
[04] CALL         [function][nil]
[05] SETTABUP     [function][nil]
[06] GETTABUP     [function][nil]
[07] GETTABUP     [function][function]
[01] GETUPVAL     [0][nil]
[02] ADD          [1][nil]
[03] SETUPVAL     [1][nil]
[04] GETUPVAL     [1][nil]
[05] RETURN       [1][nil][1]
[08] CALL         [function][function][1][2]
1
[09] CALL         [function][function]
[10] GETTABUP     [function][function]
[11] GETTABUP     [function][function]
[01] GETUPVAL     [1][nil]
[02] ADD          [2][nil]
[03] SETUPVAL     [2][nil]
[04] GETUPVAL     [2][nil]
[05] RETURN       [2][nil][2]
[12] CALL         [function][function][2][2]
2
[13] CALL         [function][function]
[14] GETTABUP     [function][function]
[01] LOADK        [0][nil]
[02] CLOSURE      [0][function]
[03] RETURN       [0][function][function]
[15] CALL         [function][function]
[16] SETTABUP     [function][function]
[17] GETTABUP     [function][function]
[18] GETTABUP     [function][function]
[01] GETUPVAL     [0][nil]
[02] ADD          [1][nil]
[03] SETUPVAL     [1][nil]
[04] GETUPVAL     [1][nil]
[05] RETURN       [1][nil][1]
[19] CALL         [function][function][1][2]
1
[20] CALL         [function][function]
[21] GETTABUP     [function][function]
[22] GETTABUP     [function][function]
[01] GETUPVAL     [2][nil]
[02] ADD          [3][nil]
[03] SETUPVAL     [3][nil]
[04] GETUPVAL     [3][nil]
[05] RETURN       [3][nil][3]
[23] CALL         [function][function][3][2]
3
[24] CALL         [function][function]
[25] GETTABUP     [function][function]
[26] GETTABUP     [function][function]
[01] GETUPVAL     [1][nil]
[02] ADD          [2][nil]
[03] SETUPVAL     [2][nil]
[04] GETUPVAL     [2][nil]
[05] RETURN       [2][nil][2]
[27] CALL         [function][function][2][2]
2
[28] CALL         [function][function]
[29] RETURN       [function][function]

7) 分析
1>
test

function newCounter ()
    local count = 0
    return function () -- 匿名函数
        count = count + 1
        return count
    end
end

c1 = newCounter()
print(c1()) --> 1

result:

env: {'print': <closure.Closure object at 0x7f2761e8fdd8>}
[01] CLOSURE      [function][nil]
[02] SETTABUP     [function][nil]
[03] GETTABUP     [function][nil]
[01] LOADK        [0][nil]
[02] CLOSURE      [0][function]
[03] RETURN       [0][function][function]
[04] CALL         [function][nil]
[05] SETTABUP     [function][nil]
[06] GETTABUP     [function][nil]
[07] GETTABUP     [function][function]
[01] GETUPVAL     [0][nil]
[02] ADD          [1][nil]
[03] SETUPVAL     [1][nil]
[04] GETUPVAL     [1][nil]
[05] RETURN       [1][nil][1]
[08] CALL         [function][function][1][2]
1
[09] CALL         [function][function]
[10] RETURN       [function][function]

增加调试信息:

set table: print <closure.Closure object at 0x7faebce59a90>
env: {'print': <closure.Closure object at 0x7faebce59a90>}
upvals:
[01] CLOSURE      [function][nil]
set table: newCounter <closure.Closure object at 0x7faebce59b00>
[02] SETTABUP     [function][nil]
get table: newCounter <closure.Closure object at 0x7faebce59b00>
[03] GETTABUP     [function][nil]
[01] LOADK        [0][nil]
upvals:
0
[02] CLOSURE      [0][function]
[03] RETURN       [0][function][function]
[04] CALL         [function][nil]
set table: c1 <closure.Closure object at 0x7faebce59f28>
[05] SETTABUP     [function][nil]
get table: print <closure.Closure object at 0x7faebce59a90>
[06] GETTABUP     [function][nil]
get table: c1 <closure.Closure object at 0x7faebce59f28>
[07] GETTABUP     [function][function]
get upval: 0
[01] GETUPVAL     [0][nil]
[02] ADD          [1][nil]
set upval: 1
[03] SETUPVAL     [1][nil]
get upval: 1
[04] GETUPVAL     [1][nil]
[05] RETURN       [1][nil][1]
[08] CALL         [function][function][1][2]
1
[09] CALL         [function][function]
[10] RETURN       [function][function]

先取到closure c1, 再从c1取upvalue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值