mirror of
https://github.com/CPunch/LuaDecompy.git
synced 2024-11-22 23:00:09 +00:00
lparser.py: proper support for locals
This commit is contained in:
parent
b8bf02f7d0
commit
0f72e71a59
36
README.md
36
README.md
@ -12,10 +12,10 @@ Lua has a relatively small instruction set (only 38 different opcodes!). This ma
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
> cat example.lua && luac5.1 -o example.luac example.lua
|
> cat example.lua && luac5.1 -o example.luac example.lua
|
||||||
i = 0
|
local i, x = 0, 2
|
||||||
|
|
||||||
while i < 10 do
|
while i < 10 do
|
||||||
print(i)
|
print(i + x)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
> python main.py example.luac
|
> python main.py example.luac
|
||||||
@ -23,34 +23,32 @@ example.luac
|
|||||||
|
|
||||||
==== [[example.lua's constants]] ====
|
==== [[example.lua's constants]] ====
|
||||||
|
|
||||||
0: [STRING] i
|
0: [NUMBER] 0.0
|
||||||
1: [NUMBER] 0.0
|
1: [NUMBER] 2.0
|
||||||
2: [NUMBER] 10.0
|
2: [NUMBER] 10.0
|
||||||
3: [STRING] print
|
3: [STRING] print
|
||||||
4: [NUMBER] 1.0
|
4: [NUMBER] 1.0
|
||||||
|
|
||||||
==== [[example.lua's dissassembly]] ====
|
==== [[example.lua's dissassembly]] ====
|
||||||
|
|
||||||
[ 0] LOADK : R[0] K[1] ; load 0.0 into R[0]
|
[ 0] LOADK : R[0] K[0] ; load 0.0 into R[0]
|
||||||
[ 1] SETGLOBAL : R[0] K[0] ;
|
[ 1] LOADK : R[1] K[1] ; load 2.0 into R[1]
|
||||||
[ 2] GETGLOBAL : R[0] K[0] ;
|
[ 2] LT : R[0] R[0] K[2] ;
|
||||||
[ 3] LT : R[0] R[0] K[2] ;
|
[ 3] JMP : R[0] 5 ;
|
||||||
[ 4] JMP : R[0] 7 ;
|
[ 4] GETGLOBAL : R[2] K[3] ;
|
||||||
[ 5] GETGLOBAL : R[0] K[3] ;
|
[ 5] ADD : R[3] R[0] R[1] ;
|
||||||
[ 6] GETGLOBAL : R[1] K[0] ;
|
[ 6] CALL : R[2] 2 1 ;
|
||||||
[ 7] CALL : R[0] 2 1 ;
|
[ 7] ADD : R[0] R[0] K[4] ;
|
||||||
[ 8] GETGLOBAL : R[0] K[0] ;
|
[ 8] JMP : R[0] -7 ;
|
||||||
[ 9] ADD : R[0] R[0] K[4] ;
|
[ 9] RETURN : R[0] 1 0 ;
|
||||||
[ 10] SETGLOBAL : R[0] K[0] ;
|
|
||||||
[ 11] JMP : R[0] -10 ;
|
|
||||||
[ 12] RETURN : R[0] 1 0 ;
|
|
||||||
|
|
||||||
==== [[example.lua's decompiled source]] ====
|
==== [[example.lua's decompiled source]] ====
|
||||||
|
|
||||||
|
|
||||||
i = 0.0
|
local i = 0.0
|
||||||
|
local x = 2.0
|
||||||
while i < 10.0 do
|
while i < 10.0 do
|
||||||
print(i)
|
print((i + x))
|
||||||
i = (i + 1.0)
|
i = (i + 1.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
57
lparser.py
57
lparser.py
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
from operator import concat
|
from operator import concat
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
|
from xmlrpc.client import Boolean
|
||||||
from lundump import Chunk, Constant, Instruction, Opcodes, whichRK, readRKasK
|
from lundump import Chunk, Constant, Instruction, Opcodes, whichRK, readRKasK
|
||||||
|
|
||||||
class _Scope:
|
class _Scope:
|
||||||
@ -15,6 +16,12 @@ class _Scope:
|
|||||||
self.startPC = startPC
|
self.startPC = startPC
|
||||||
self.endPC = endPC
|
self.endPC = endPC
|
||||||
|
|
||||||
|
class _Traceback:
|
||||||
|
def __init__(self):
|
||||||
|
self.sets = []
|
||||||
|
self.uses = []
|
||||||
|
self.isConst = False
|
||||||
|
|
||||||
class LuaDecomp:
|
class LuaDecomp:
|
||||||
def __init__(self, chunk: Chunk):
|
def __init__(self, chunk: Chunk):
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
@ -22,13 +29,16 @@ class LuaDecomp:
|
|||||||
self.scope = []
|
self.scope = []
|
||||||
self.top = {}
|
self.top = {}
|
||||||
self.locals = {}
|
self.locals = {}
|
||||||
|
self.traceback = {}
|
||||||
self.unknownLocalCount = 0
|
self.unknownLocalCount = 0
|
||||||
self.src: str = ""
|
self.src: str = ""
|
||||||
|
|
||||||
# configurations!
|
# configurations!
|
||||||
self.aggressiveLocals = False # should *EVERY* accessed register be considered a local?
|
self.aggressiveLocals = False # should *EVERY* set register be considered a local?
|
||||||
self.indexWidth = 4 # how many spaces for indentions?
|
self.indexWidth = 4 # how many spaces for indentions?
|
||||||
|
|
||||||
|
self.__loadLocals()
|
||||||
|
|
||||||
# parse instructions
|
# parse instructions
|
||||||
while self.pc < len(self.chunk.instructions):
|
while self.pc < len(self.chunk.instructions):
|
||||||
self.parseInstr()
|
self.parseInstr()
|
||||||
@ -54,6 +64,33 @@ class LuaDecomp:
|
|||||||
def __getCurrInstr(self) -> Instruction:
|
def __getCurrInstr(self) -> Instruction:
|
||||||
return self.__getInstrAtPC(self.pc)
|
return self.__getInstrAtPC(self.pc)
|
||||||
|
|
||||||
|
# when we read from a register, call this
|
||||||
|
def __addUseTraceback(self, reg: int) -> None:
|
||||||
|
if not self.pc in self.traceback:
|
||||||
|
self.traceback[self.pc] = _Traceback()
|
||||||
|
|
||||||
|
self.traceback[self.pc].uses.append(reg)
|
||||||
|
|
||||||
|
# when we write from a register, call this
|
||||||
|
def __addSetTraceback(self, reg: int) -> None:
|
||||||
|
if not self.pc in self.traceback:
|
||||||
|
self.traceback[self.pc] = _Traceback()
|
||||||
|
|
||||||
|
self.traceback[self.pc].sets.append(reg)
|
||||||
|
|
||||||
|
# walks traceback, if local wasn't set before, the local needs to be defined
|
||||||
|
def __needsDefined(self, reg) -> Boolean:
|
||||||
|
for _, trace in self.traceback.items():
|
||||||
|
if reg in trace.sets:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# wasn't set in traceback! needs defined!
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __loadLocals(self):
|
||||||
|
for i in range(len(self.chunk.locals)):
|
||||||
|
self.locals[i] = self.chunk.locals[i].name
|
||||||
|
|
||||||
def __addExpr(self, code: str) -> None:
|
def __addExpr(self, code: str) -> None:
|
||||||
self.src += code
|
self.src += code
|
||||||
|
|
||||||
@ -61,22 +98,34 @@ class LuaDecomp:
|
|||||||
self.src += '\n' + (' ' * self.indexWidth * len(self.scope))
|
self.src += '\n' + (' ' * self.indexWidth * len(self.scope))
|
||||||
|
|
||||||
def __getReg(self, indx: int) -> str:
|
def __getReg(self, indx: int) -> str:
|
||||||
|
self.__addUseTraceback(indx)
|
||||||
|
|
||||||
# if the top indx is a local, get it
|
# if the top indx is a local, get it
|
||||||
return self.locals[indx] if indx in self.locals else self.top[indx]
|
return self.locals[indx] if indx in self.locals else self.top[indx]
|
||||||
|
|
||||||
def __setReg(self, indx: int, code: str) -> None:
|
def __setReg(self, indx: int, code: str) -> None:
|
||||||
# if the top indx is a local, set it
|
# if the top indx is a local, set it
|
||||||
if indx in self.locals:
|
if indx in self.locals:
|
||||||
self.__startStatement()
|
if self.__needsDefined(indx):
|
||||||
self.__addExpr(self.locals[indx] + " = " + code)
|
self.__newLocal(indx, code)
|
||||||
|
else:
|
||||||
|
self.__startStatement()
|
||||||
|
self.__addExpr(self.locals[indx] + " = " + code)
|
||||||
elif self.aggressiveLocals: # 'every register is a local!!'
|
elif self.aggressiveLocals: # 'every register is a local!!'
|
||||||
self.__newLocal(indx, code)
|
self.__newLocal(indx, code)
|
||||||
|
|
||||||
|
|
||||||
|
self.__addSetTraceback(indx)
|
||||||
self.top[indx] = code
|
self.top[indx] = code
|
||||||
|
|
||||||
# ========================================[[ Locals ]]=========================================
|
# ========================================[[ Locals ]]=========================================
|
||||||
|
|
||||||
def __makeLocalIdentifier(self, indx: int) -> str:
|
def __makeLocalIdentifier(self, indx: int) -> str:
|
||||||
|
# first, check if we have a local name already determined
|
||||||
|
if indx in self.locals:
|
||||||
|
return self.locals[indx]
|
||||||
|
|
||||||
|
# otherwise, generate a local
|
||||||
self.locals[indx] = "__unknLocal%d" % self.unknownLocalCount
|
self.locals[indx] = "__unknLocal%d" % self.unknownLocalCount
|
||||||
self.unknownLocalCount += 1
|
self.unknownLocalCount += 1
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user