1)lua table
from lua_value import LuaValue
class LuaTable:
def __init__(self, narr, nrec):
self.arr = None
self.map = None
if narr > 0:
self.arr = []
if nrec > 0:
self.map = {}
def __len__(self):
return len(self.arr) if self.arr is not None else 0
def get(self, key):
key = LuaValue.float2integer(key)
if self.arr and isinstance(key, int):
if 1 <= key <= len(self.arr):
return self.arr[key-1]
return self.map[key] if key in self.map else None
def map_to_arr(self):
if self.map:
i = len(self.arr)
while i in self.map:
v = self.map[i]
self.map.pop(i)
self.arr.append(v)
i += 1
def put(self, key, val):
assert(key is not None)
assert(key is not float('NAN'))
key = LuaValue.float2integer(key)
if isinstance(key, int) and key >= 1:
if not self.arr:
self.arr = []
arr_len = len(self.arr)
if key <= arr_len:
self.arr[key-1] = val
if key == arr_len and val is None:
self.arr.pop(key)
return
if key == arr_len + 1:
if self.map:
self.map.pop(key)
if val is not None:
self.arr.append(val)
self.map_to_arr()
return
if val is not None:
if not self.map:
self.map = {key: val}
else:
self.map.pop(key)
2)lua state
from lua_stack import LuaStack
from lua_type import LuaType
from lua_value import LuaValue
from arith_op import ArithOp
from arithmetic import Arithmetic
from cmp_op import CmpOp
from compare import Compare
from lua_table import LuaTable
class LuaState:
def __init__(self, proto):
self.stack = LuaStack()
self.proto = proto
self.pc = 0
# ...
# table
def create_table(self, narr, nrec):
table = LuaTable(narr, nrec)
self.stack.push(table)
@staticmethod
def get_table_val(t, k):
if isinstance(t, LuaTable):
return t.get(k)
raise Exception('not a table')
def get_table(self, idx):
t = self.stack.get(idx)
k = self.stack.pop()
v = LuaState.get_table_val(t, k)
self.stack.push(v)
return LuaValue.type_of(v)
def get_i(self, idx, i):
t = self.stack.get(idx)
v = LuaState.get_table_val(t, i)
self.stack.push(v)
return LuaValue.type_of(v)
def set_table(self, idx):
t = self.stack.get(idx)
v = self.stack.pop()
k = self.stack.pop()
LuaState.set_table_kv(t, k, v)
@staticmethod
def set_table_kv(t, k, v):
if isinstance(t, LuaTable):
t.put(k, v)
return
raise Exception('not a table')
def set_field(self, idx, k):
t = self.stack.get(idx)
v = self.stack.pop()
LuaState.set_table_kv(t, k, v)
def set_i(self, idx, i):
t = self.stack.get(idx)
v = self.stack.pop()
LuaState.set_table_kv(t, i, v)
3)instruction
# R(A) := {}
def newtable(inst, vm):
a, b, c = inst.a_b_c()
a += 1
vm.create_table(LuaValue.fb2int(b), LuaValue.fb2int(c))
vm.replace(a)
# R(A) := R(B)[RK(C)]
def gettable(inst, vm):
a, b, c = inst.a_b_c()
a += 1
b += 1
vm.get_rk(c)
vm.get_table(b)
vm.replace(a)
# R(A)[R(B)] := RK(C)
def settable(inst, vm):
a, b, c = inst.a_b_c()
a += 1
vm.get_rk(b)
vm.get_rk(c)
vm.set_table(a)
# R(A)[(C-1)*LFIELDS_PER_FLUSH+i] := R(A+i), 1 <= i <= B
def setlist(inst, vm):
a, b, c = inst.a_b_c()
a += 1
c = c - 1 if c > 0 else inst.ax(vm.fetch())
vm.check_stack(1)
idx = c * LFIELDS_PER_FLUSH
for i in range(1, b+1):
idx += 1
vm.push_value(a+i)
vm.set_i(a, idx)
op_codes = [
# T A B C mode name action
OpCode(0, 1, OpArgR, OpArgN, IABC, "MOVE ", move), # R(A) := R(B)
OpCode(0, 1, OpArgK, OpArgN, IABx, "LOADK ", loadk), # R(A) := Kst(Bx)
OpCode(0, 1, OpArgN, OpArgN, IABx, "LOADKX ", loadkx), # R(A) := Kst(extra arg)
OpCode(0, 1, OpArgU, OpArgU, IABC, "LOADBOOL", loadbool), # R(A) := (bool)B; if (C) pc++
OpCode(0, 1, OpArgU, OpArgN, IABC, "LOADNIL ", loadnil), # R(A), R(A+1), ..., R(A+B) := nil
OpCode(0, 1, OpArgU, OpArgN, IABC, "GETUPVAL", None), # R(A) := UpValue[B]
OpCode(0, 1, OpArgU, OpArgK, IABC, "GETTABUP", None), # R(A) := UpValue[B][RK(C)]
OpCode(0, 1, OpArgR, OpArgK, IABC, "GETTABLE", gettable), # R(A) := R(B)[RK(C)]
OpCode(0, 0, OpArgK, OpArgK, IABC, "SETTABUP", None), # UpValue[A][RK(B)] := RK(C)
OpCode(0, 0, OpArgU, OpArgN, IABC, "SETUPVAL", None), # UpValue[B] := R(A)
OpCode(0, 0, OpArgK, OpArgK, IABC, "SETTABLE", settable), # R(A)[RK(B)] := RK(C)
OpCode(0, 1, OpArgU, OpArgU, IABC, "NEWTABLE", newtable), # R(A) := {} (size = B,C)
OpCode(0, 1, OpArgR, OpArgK, IABC, "SELF ", None), # R(A+1) := R(B); R(A) := R(B)[RK(C)]
OpCode(0, 1, OpArgK, OpArgK, IABC, "ADD ", add), # R(A) := RK(B) + RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "SUB ", sub), # R(A) := RK(B) - RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "MUL ", mul), # R(A) := RK(B) * RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "MOD ", mod), # R(A) := RK(B) % RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "POW ", luapow), # R(A) := RK(B) ^ RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "DIV ", div), # R(A) := RK(B) / RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "IDIV ", idiv), # R(A) := RK(B) // RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "BAND ", band), # R(A) := RK(B) & RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "BOR ", bor), # R(A) := RK(B) | RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "BXOR ", bxor), # R(A) := RK(B) ~ RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "SHL ", shl), # R(A) := RK(B) << RK(C)
OpCode(0, 1, OpArgK, OpArgK, IABC, "SHR ", shr), # R(A) := RK(B) >> RK(C)
OpCode(0, 1, OpArgR, OpArgN, IABC, "UNM ", unm), # R(A) := -R(B)
OpCode(0, 1, OpArgR, OpArgN, IABC, "BNOT ", bnot), # R(A) := ~R(B)
OpCode(0, 1, OpArgR, OpArgN, IABC, "NOT ", luanot), # R(A) := not R(B)
OpCode(0, 1, OpArgR, OpArgN, IABC, "LEN ", length), # R(A) := length of R(B)
OpCode(0, 1, OpArgR, OpArgR, IABC, "CONCAT ", concat), # R(A) := R(B).. ... ..R(C)
OpCode(0, 0, OpArgR, OpArgN, IAsBx, "JMP ", jmp), # pc+=sBx; if (A) close all upvalues >= R(A - 1)
OpCode(1, 0, OpArgK, OpArgK, IABC, "EQ ", eq), # if ((RK(B) == RK(C)) ~= A) then pc++
OpCode(1, 0, OpArgK, OpArgK, IABC, "LT ", lt), # if ((RK(B) < RK(C)) ~= A) then pc++
OpCode(1, 0, OpArgK, OpArgK, IABC, "LE ", le), # if ((RK(B) <= RK(C)) ~= A) then pc++
OpCode(1, 0, OpArgN, OpArgU, IABC, "TEST ", test), # if not (R(A) <=> C) then pc++
OpCode(1, 1, OpArgR, OpArgU, IABC, "TESTSET ", testset), # if (R(B) <=> C) then R(A) := R(B) else pc++
OpCode(0, 1, OpArgU, OpArgU, IABC, "CALL ", None), # R(A), ...,R(A+C-2) := R(A)(R(A+1), ...,R(A+B-1))
OpCode(0, 1, OpArgU, OpArgU, IABC, "TAILCALL", None), # return R(A)(R(A+1), ... ,R(A+B-1))
OpCode(0, 0, OpArgU, OpArgN, IABC, "RETURN ", None), # return R(A), ... ,R(A+B-2)
OpCode(0, 1, OpArgR, OpArgN, IAsBx, "FORLOOP ", forloop), # R(A)+=R(A+2); if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
OpCode(0, 1, OpArgR, OpArgN, IAsBx, "FORPREP ", forprep), # R(A)-=R(A+2); pc+=sBx
OpCode(0, 0, OpArgN, OpArgU, IABC, "TFORCALL", None), # R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
OpCode(0, 1, OpArgR, OpArgN, IAsBx, "TFORLOOP", None), # if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
OpCode(0, 0, OpArgU, OpArgU, IABC, "SETLIST ", setlist), # R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
OpCode(0, 1, OpArgU, OpArgN, IABx, "CLOSURE ", None), # R(A) := closure(KPROTO[Bx])
OpCode(0, 1, OpArgU, OpArgN, IABC, "VARARG ", None), # R(A), R(A+1), ..., R(A+B-2) = vararg
OpCode(0, 0, OpArgU, OpArgU, IAx, "EXTRAARG", None), # extra (larger) argument for previous opcode
]
4)test
local t = {"a", "b", "c"}
t[2] = "B"
t["foo"] = "Bar"
local s = t[3] .. t[2] .. t[1] .. t["foo"] .. #t
from binary_chunk import BinaryChunk
from lua_state import LuaState
from opcode import Instruction
from opcode import OpCode
def lua_main(proto):
vm = LuaState(proto)
vm.set_top(proto.get_max_stack_size())
while True:
pc = vm.get_pc()
i = vm.fetch()
inst = Instruction(i)
if inst.op_code() != OpCode.RETURN:
inst.execute(vm)
print('[%02d] %-8s ' % (pc+1, inst.op_name()), end='')
vm.print_stack()
else:
break
def main():
bc = BinaryChunk('./test/table.luac')
bc.print_header()
bc.check_header()
bc.print_main_func()
proto = bc.get_main_func()
lua_main(proto)
if __name__ == '__main__':
main()
5)result
signature: b'\x1bLua'
version: 83
format: 0
luac_data: b'\x19\x93\r\n\x1a\n'
cint_size: 4
csizet_size: 8
inst_size: 4
lua_int_size: 8
lua_number_size: 8
luac_int: 0x5678
luac_num: 370.5
main <@table.lua:0,0> (14 instructions)
0+ params, 6 slots, 1 upvalues, 2 locals, 9 constants, 0 functions
1 [1] NEWTABLE 0 3 0
2 [1] LOADK 1 -1
3 [1] LOADK 2 -2
4 [1] LOADK 3 -3
5 [1] SETLIST 0 3 1
6 [2] SETTABLE 0 -4 -5
7 [3] SETTABLE 0 -6 -7
8 [4] GETTABLE 1 0 -8
9 [4] GETTABLE 2 0 -4
10 [4] GETTABLE 3 0 -9
11 [4] GETTABLE 4 0 -6
12 [4] LEN 5 0
13 [4] CONCAT 1 1 5
14 [4] RETURN 0 1
constants (9):
1 "a"
2 "b"
3 "c"
4 2
5 "B"
6 "foo"
7 "Bar"
8 3
9 1
locals (2):
1 t 6 15
2 s 14 15
upvalues (1):
1 _ENV 1 0
[01] NEWTABLE [table][nil][nil][nil][nil][nil]
[02] LOADK [table]["a"][nil][nil][nil][nil]
[03] LOADK [table]["a"]["b"][nil][nil][nil]
[04] LOADK [table]["a"]["b"]["c"][nil][nil]
[05] SETLIST [table]["a"]["b"]["c"][nil][nil]
[06] SETTABLE [table]["a"]["b"]["c"][nil][nil]
[07] SETTABLE [table]["a"]["b"]["c"][nil][nil]
[08] GETTABLE [table]["c"]["b"]["c"][nil][nil]
[09] GETTABLE [table]["c"]["B"]["c"][nil][nil]
[10] GETTABLE [table]["c"]["B"]["a"][nil][nil]
[11] GETTABLE [table]["c"]["B"]["a"]["Bar"][nil]
[12] LEN [table]["c"]["B"]["a"]["Bar"][3]
[13] CONCAT [table]["cBaBar3"]["B"]["a"]["Bar"][3]