modification of LIST_APPEND opcode handling

This commit is contained in:
Mysterie
2012-12-14 17:22:59 +01:00
parent 60869b0399
commit 3956a5a709
7 changed files with 308 additions and 177 deletions

View File

@@ -13,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
hasArgumentExtended = []
PJIF = PJIT = JA = JF = 0
opmap = {}
@@ -37,6 +38,10 @@ def jabs_op(name, op):
def_op(name, op)
hasjabs.append(op)
def def_extArg(name, op):
def_op(name, op)
hasArgumentExtended.append(op)
def updateGlobal():
globals().update({'PJIF': opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': opmap['JUMP_IF_TRUE']})
@@ -63,7 +68,7 @@ def_op('UNARY_CONVERT', 13) # 25
def_op('UNARY_INVERT', 15) # 34
def_op('LIST_APPEND', 18) # 68
def_extArg('LIST_APPEND', 18) # 68
def_op('BINARY_POWER', 19) # 28
def_op('BINARY_MULTIPLY', 20) # 36
def_op('BINARY_DIVIDE', 21) # 12

View File

@@ -13,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
hasArgumentExtended = []
PJIF = PJIT = JA = JF = 0
opmap = {}
@@ -37,6 +38,10 @@ def jabs_op(name, op):
def_op(name, op)
hasjabs.append(op)
def def_extArg(name, op):
def_op(name, op)
hasArgumentExtended.append(op)
def updateGlobal():
globals().update({'PJIF': opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': opmap['JUMP_IF_TRUE']})
@@ -63,7 +68,7 @@ def_op('UNARY_CONVERT', 13)
def_op('UNARY_INVERT', 15)
def_op('LIST_APPEND', 18)
def_extArg('LIST_APPEND', 18)
def_op('BINARY_POWER', 19)
def_op('BINARY_MULTIPLY', 20)
def_op('BINARY_DIVIDE', 21)

View File

@@ -13,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
hasArgumentExtended = []
PJIF = PJIT = JA = JF = 0
opmap = {}
@@ -133,6 +134,7 @@ name_op('DELETE_NAME', 91) # ""
def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
jrel_op('FOR_ITER', 93)
def_op('LIST_APPEND', 94)
name_op('STORE_ATTR', 95) # Index in name list
name_op('DELETE_ATTR', 96) # ""
name_op('STORE_GLOBAL', 97) # ""

View File

@@ -208,11 +208,14 @@ class Scanner(object):
return result
def op_size(self, op):
if op < self.opc.HAVE_ARGUMENT:
if op < self.opc.HAVE_ARGUMENT and op not in self.opc.hasArgumentExtended:
return 1
else:
return 3
def op_hasArgument(self, op):
return self.op_size(op) > 1
def op_range(self, start, end):
while start < end:
yield start

View File

@@ -31,12 +31,9 @@ class Scanner25(scan.Scanner):
customize = {}
Token = self.Token # shortcut
self.code = array('B', co.co_code)
n = len(self.code)
# linestarts contains bloc code adresse (addr,block)
self.linestarts = list(dis.findlinestarts(co))
self.prev = [0]
# change jump struct
self.restructRelativeJump()
# class and names
if classname:
@@ -54,38 +51,16 @@ class Scanner25(scan.Scanner):
names = co.co_names
varnames = co.co_varnames
self.names = names
# add instruction to remonde in "toDel" list
toDel = []
# add instruction to change in "toChange" list
# list of instruction to remove/add or change to match with bytecode 2.7
self.toChange = []
for i in self.op_range(0, n):
op = self.code[i]
ret = self.getOpcodeToDel(i)
if ret != None:
toDel += ret
#self.print_bytecode()
if toDel:
toDel = sorted(list(set(toDel)))
delta = 0
self.restructCode(toDel)
for x in toDel:
if self.code[x-delta] >= HAVE_ARGUMENT:
self.code.pop(x-delta)
self.code.pop(x-delta)
self.code.pop(x-delta)
delta += 3
else:
self.code.pop(x-delta)
delta += 1
self.restructBytecode()
codelen = len(self.code)
# mapping adresses of prev instru
n = len(self.code)
for i in self.op_range(0, n):
for i in self.op_range(0, codelen):
op = self.code[i]
self.prev.append(i)
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
self.prev.append(i)
self.prev.append(i)
j = 0
@@ -100,17 +75,16 @@ class Scanner25(scan.Scanner):
j += 1
last_op = self.code[self.prev[start_byte]]
(prev_start_byte, prev_line_no) = (start_byte, line_no)
while j < n:
self.lines.append(linetuple(prev_line_no, n))
while j < codelen:
self.lines.append(linetuple(prev_line_no, codelen))
j+=1
# self.lines contains (block,addrLastInstr)
cf = self.find_jump_targets(self.code)
# contains (code, [addrRefToCode])
last_stmt = self.next_stmt[0]
i = self.next_stmt[last_stmt]
replace = {}
while i < n-1:
while i < codelen-1:
if self.lines[last_stmt].next > i:
if self.code[last_stmt] == PRINT_ITEM:
if self.code[i] == PRINT_ITEM:
@@ -120,7 +94,7 @@ class Scanner25(scan.Scanner):
last_stmt = i
i = self.next_stmt[i]
imports = self.all_instr(0, n, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR))
imports = self.all_instr(0, codelen, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR))
if len(imports) > 1:
last_import = imports[0]
for i in imports[1:]:
@@ -130,7 +104,7 @@ class Scanner25(scan.Scanner):
last_import = i
extended_arg = 0
for offset in self.op_range(0, n):
for offset in self.op_range(0, codelen):
op = self.code[offset]
op_name = opname[op]
oparg = None; pattr = None
@@ -141,7 +115,7 @@ class Scanner25(scan.Scanner):
rv.append(Token('COME_FROM', None, repr(j),
offset="%s_%d" % (offset, k) ))
k += 1
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
oparg = self.get_argument(offset) + extended_arg
extended_arg = 0
if op == EXTENDED_ARG:
@@ -238,6 +212,10 @@ class Scanner25(scan.Scanner):
'''
opcode = self.code[i]
opsize = self.op_size(opcode)
if i+opsize >= len(self.code):
return None
if opcode == EXTENDED_ARG:
raise 'TODO'
# del POP_TOP
@@ -288,7 +266,7 @@ class Scanner25(scan.Scanner):
end += self.op_size(LOAD_FAST)
# log JA/POP_TOP to del and update PJIF
while start < end:
start = self.first_instr(start, len(self.code), (PJIF,PJIT))
start = self.first_instr(start, end, (PJIF,PJIT)) # end = len(self.code)
if start == None: break
target = self.get_target(start)
if self.code[target] == POP_TOP and self.code[target-3] == JA:
@@ -357,65 +335,144 @@ class Scanner25(scan.Scanner):
return toDel
return None
def restructRelativeJump(self):
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
'''
def getOpcodeToExp(self):
# we handle listExp, if opcode have to be resized
listExp = []
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if op in self.opc.hasArgumentExtended:
listExp += [i]
elif self.op_hasArgument(op):
i+=2
i+=1
return listExp
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i)
target += i + 3
self.restructJump(i, target)
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_target(i)
if self.code[target] == JA:
target = self.get_target(target)
self.restructJump(i, target)
def restructCode(self, listDel):
def restructCode(self, listDel, listExp):
'''
restruct linestarts and jump destination after removing bad opcode
restruct linestarts and jump destination
'''
# restruct linestarts with deleted / modificated opcode
result = list()
for block in self.linestarts:
startBlock = 0
for toDel in listDel:
if toDel < block[0]:
startBlock -= self.op_size(self.code[toDel])
else:
break
for toExp in listExp:
if toExp < block[0]:
startBlock += 2
result.append((block[0]+startBlock, block[1]))
self.linestarts = result
# handle opcodeToChange deplacement
for index in xrange(len(self.toChange)):
change = self.toChange[index]
delta = 0
for toDel in listDel:
if change > toDel:
delta += self.op_size(self.code[toDel])
else:
break
self.toChange[index] -= delta
delta -= self.op_size(self.code[toDel])
for toExp in listExp:
if change > toExp:
delta += 2
self.toChange[index] += delta
# restruct jmp opcode
if listDel:
for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp]
if op in hasjrel+hasjabs: # jmp
if op in hasjrel+hasjabs:
offset = 0
jmpTarget = self.get_target(jmp)
for toDel in listDel:
if toDel < jmpTarget:
if op in hasjabs:
offset-=self.op_size(self.code[toDel])
elif jmp < toDel:
if op in hasjabs or jmp < toDel:
offset-=self.op_size(self.code[toDel])
self.restructJump(jmp, jmpTarget+offset)
if listExp:
jmp = 0
while jmp < len(self.code): # we can't use op_range for the moment
op = self.code[jmp]
if op in hasjrel+hasjabs:
offset = 0
jmpTarget = self.get_target(jmp)
for toExp in listExp:
if toExp < jmpTarget:
if op in hasjabs or jmp < toExp:
offset+=2
self.restructJump(jmp, jmpTarget+offset)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
jmp += 3
else: jmp += 1
def restructBytecode(self):
'''
add/change/delete bytecode for suiting bytecode 2.7
'''
# we can't use op_range for the moment
# convert jump opcode to 2.7
self.restructRelativeJump()
listExp = self.getOpcodeToExp()
# change code structure
if listExp:
listExp = sorted(list(set(listExp)))
self.restructCode([], listExp)
# we add arg to expended opcode
offset=0
for toExp in listExp:
self.code.insert(toExp+offset+1, 0)
self.code.insert(toExp+offset+1, 0)
offset+=2
# op_range is now ok :)
# add instruction to change in "toChange" list + MAJ toDel
listDel = []
for i in self.op_range(0, len(self.code)):
ret = self.getOpcodeToDel(i)
if ret != None:
listDel += ret
# change code structure after deleting byte
if listDel:
listDel = sorted(list(set(listDel)))
self.restructCode(listDel, [])
# finaly we delete useless opcode
delta = 0
for x in listDel:
if self.op_hasArgument(self.code[x-delta]):
self.code.pop(x-delta)
self.code.pop(x-delta)
self.code.pop(x-delta)
delta += 3
else:
break
self.restructJump(jmp, self.get_target(jmp)+offset)
self.code.pop(x-delta)
delta += 1
def restructRelativeJump(self):
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
'''
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if(op in (PJIF,PJIT)):
target = self.get_argument(i)
target += i + 3
self.restructJump(i, target)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
i += 3
else: i += 1
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if(op in (PJIF,PJIT)):
target = self.get_target(i)
if self.code[target] == JA:
target = self.get_target(target)
self.restructJump(i, target)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
i += 3
else: i += 1
def restructJump(self, pos, newTarget):
if not (self.code[pos] in hasjabs+hasjrel):
@@ -442,7 +499,7 @@ class Scanner25(scan.Scanner):
STORE_ATTR, DELETE_ATTR, STORE_SUBSCR, DELETE_SUBSCR,
RETURN_VALUE, RAISE_VARARGS, POP_TOP,
PRINT_EXPR, PRINT_ITEM, PRINT_NEWLINE, PRINT_ITEM_TO, PRINT_NEWLINE_TO,
JUMP_ABSOLUTE, EXEC_STMT,
JUMP_ABSOLUTE, EXEC_STMT
}
stmt_opcode_seqs = [(PJIF, JF), (PJIF, JA), (PJIT, JF), (PJIT, JA)]
@@ -539,8 +596,6 @@ class Scanner25(scan.Scanner):
in python2.3+
'''
# TODO: check the struct boundaries more precisely -Dan
code = self.code
# Ev remove this test and make op a mandatory argument -Dan
if op is None:
@@ -630,7 +685,6 @@ class Scanner25(scan.Scanner):
end = self.restrict_to_parent(target, parent)
if target != end:
self.fixed_jumps[pos] = end
#print target, end, parent
## Add the try block
self.structs.append({'type': 'try',
'start': start,
@@ -808,7 +862,7 @@ class Scanner25(scan.Scanner):
## Determine structures and fix jumps for 2.3+
self.detect_structure(i, op)
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
label = self.fixed_jumps.get(i)
oparg = self.get_argument(i)
if label is None:

View File

@@ -32,12 +32,9 @@ class Scanner26(scan.Scanner):
customize = {}
Token = self.Token # shortcut
self.code = array('B', co.co_code)
n = len(self.code)
# linestarts contains bloc code adresse (addr,block)
self.linestarts = list(dis.findlinestarts(co))
self.prev = [0]
# change jump struct
self.restructRelativeJump()
# class and names
if classname:
classname = '_' + classname.lstrip('_') + '__'
@@ -55,41 +52,19 @@ class Scanner26(scan.Scanner):
varnames = co.co_varnames
self.names = names
# add instruction to remonde in "toDel" list
toDel = []
# add instruction to change in "toChange" list
# list of instruction to remove/add or change to match with bytecode 2.7
self.toChange = []
for i in self.op_range(0, n):
op = self.code[i]
ret = self.getOpcodeToDel(i)
if ret != None:
toDel += ret
if toDel:
toDel = sorted(list(set(toDel)))
delta = 0
self.restructCode(toDel)
for x in toDel:
if self.code[x-delta] >= HAVE_ARGUMENT:
self.code.pop(x-delta)
self.code.pop(x-delta)
self.code.pop(x-delta)
delta += 3
else:
self.code.pop(x-delta)
delta += 1
self.restructBytecode()
codelen = len(self.code)
# mapping adresses of prev instru
n = len(self.code)
for i in self.op_range(0, n):
for i in self.op_range(0, codelen):
op = self.code[i]
self.prev.append(i)
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
self.prev.append(i)
self.prev.append(i)
j = 0
linestarts = self.linestarts
self.lines = []
linetuple = namedtuple('linetuple', ['l_no', 'next'])
linestartoffsets = {a for (a, _) in linestarts}
@@ -100,8 +75,8 @@ class Scanner26(scan.Scanner):
j += 1
last_op = self.code[self.prev[start_byte]]
(prev_start_byte, prev_line_no) = (start_byte, line_no)
while j < n:
self.lines.append(linetuple(prev_line_no, n))
while j < codelen:
self.lines.append(linetuple(prev_line_no, codelen))
j+=1
# self.lines contains (block,addrLastInstr)
cf = self.find_jump_targets(self.code)
@@ -110,7 +85,7 @@ class Scanner26(scan.Scanner):
last_stmt = self.next_stmt[0]
i = self.next_stmt[last_stmt]
replace = {}
while i < n-1:
while i < codelen-1:
if self.lines[last_stmt].next > i:
if self.code[last_stmt] == PRINT_ITEM:
if self.code[i] == PRINT_ITEM:
@@ -120,7 +95,7 @@ class Scanner26(scan.Scanner):
last_stmt = i
i = self.next_stmt[i]
imports = self.all_instr(0, n, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR))
imports = self.all_instr(0, codelen, (IMPORT_NAME, IMPORT_FROM, IMPORT_STAR))
if len(imports) > 1:
last_import = imports[0]
for i in imports[1:]:
@@ -130,7 +105,7 @@ class Scanner26(scan.Scanner):
last_import = i
extended_arg = 0
for offset in self.op_range(0, n):
for offset in self.op_range(0, codelen):
op = self.code[offset]
op_name = opname[op]
oparg = None; pattr = None
@@ -141,7 +116,7 @@ class Scanner26(scan.Scanner):
rv.append(Token('COME_FROM', None, repr(j),
offset="%s_%d" % (offset, k) ))
k += 1
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
oparg = self.get_argument(offset) + extended_arg
extended_arg = 0
if op == EXTENDED_ARG:
@@ -296,7 +271,7 @@ class Scanner26(scan.Scanner):
end += self.op_size(LOAD_FAST)
# log JA/POP_TOP to del and update PJIF
while start < end:
start = self.first_instr(start, len(self.code), (PJIF,PJIT))
start = self.first_instr(start, end, (PJIF,PJIT))
if start == None: break
target = self.get_target(start)
if self.code[target] == POP_TOP and self.code[target-3] == JA:
@@ -356,63 +331,144 @@ class Scanner26(scan.Scanner):
return toDel
return None
def restructRelativeJump(self):
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
'''
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i)
target += i + 3
self.restructJump(i, target)
def getOpcodeToExp(self):
# we handle listExp, if opcode have to be resized
listExp = []
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if op in self.opc.hasArgumentExtended:
listExp += [i]
elif self.op_hasArgument(op):
i+=2
i+=1
return listExp
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_target(i)
if self.code[target] == JA:
target = self.get_target(target)
self.restructJump(i, target)
def restructCode(self, listDel):
def restructCode(self, listDel, listExp):
'''
restruct linestarts and jump destination after removing a POP_TOP
restruct linestarts and jump destination after converting bytecode
'''
# restruct linestarts with deleted / modificated opcode
result = list()
for block in self.linestarts:
startBlock = 0
for toDel in listDel:
if toDel < block[0]:
startBlock -= self.op_size(self.code[toDel])
else:
break
for toExp in listExp:
if toExp < block[0]:
startBlock += 2
result.append((block[0]+startBlock, block[1]))
self.linestarts = result
# handle opcodeToChange deplacement
for index in xrange(len(self.toChange)):
change = self.toChange[index]
delta = 0
for toDel in listDel:
if change > toDel:
delta += self.op_size(self.code[toDel])
else:
break
self.toChange[index] -= delta
delta -= self.op_size(self.code[toDel])
for toExp in listExp:
if change > toExp:
delta += 2
self.toChange[index] += delta
# restruct jmp opcode
if listDel:
for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp]
if op in hasjrel+hasjabs: # jmp
if op in hasjrel+hasjabs:
offset = 0
jmpTarget = self.get_target(jmp)
for toDel in listDel:
if toDel < jmpTarget:
if op in hasjabs:
offset-=self.op_size(self.code[toDel])
elif jmp < toDel:
if op in hasjabs or jmp < toDel:
offset-=self.op_size(self.code[toDel])
self.restructJump(jmp, jmpTarget+offset)
if listExp:
jmp = 0
while jmp < len(self.code): # we can't use op_range for the moment
op = self.code[jmp]
if op in hasjrel+hasjabs:
offset = 0
jmpTarget = self.get_target(jmp)
for toExp in listExp:
if toExp < jmpTarget:
if op in hasjabs or jmp < toExp:
offset+=2
self.restructJump(jmp, jmpTarget+offset)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
jmp += 3
else: jmp += 1
def restructBytecode(self):
'''
add/change/delete bytecode for suiting bytecode 2.7
'''
# we can't use op_range for the moment
# convert jump opcode to 2.7
self.restructRelativeJump()
listExp = self.getOpcodeToExp()
# change code structure
if listExp:
listExp = sorted(list(set(listExp)))
self.restructCode([], listExp)
# we add arg to expended opcode
offset=0
for toExp in listExp:
self.code.insert(toExp+offset+1, 0)
self.code.insert(toExp+offset+1, 0)
offset+=2
# op_range is now ok :)
# add instruction to change in "toChange" list + MAJ toDel
listDel = []
for i in self.op_range(0, len(self.code)):
ret = self.getOpcodeToDel(i)
if ret != None:
listDel += ret
# change code structure after deleting byte
if listDel:
listDel = sorted(list(set(listDel)))
self.restructCode(listDel, [])
# finaly we delete useless opcode
delta = 0
for x in listDel:
if self.op_hasArgument(self.code[x-delta]):
self.code.pop(x-delta)
self.code.pop(x-delta)
self.code.pop(x-delta)
delta += 3
else:
break
self.restructJump(jmp, self.get_target(jmp)+offset)
self.code.pop(x-delta)
delta += 1
def restructRelativeJump(self):
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
'''
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if(op in (PJIF,PJIT)):
target = self.get_argument(i)
target += i + 3
self.restructJump(i, target)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
i += 3
else: i += 1
i=0
while i < len(self.code): # we can't use op_range for the moment
op = self.code[i]
if(op in (PJIF,PJIT)):
target = self.get_target(i)
if self.code[target] == JA:
target = self.get_target(target)
self.restructJump(i, target)
if self.op_hasArgument(op) and op not in self.opc.hasArgumentExtended:
i += 3
else: i += 1
def restructJump(self, pos, newTarget):
if not (self.code[pos] in hasjabs+hasjrel):
@@ -805,7 +861,7 @@ class Scanner26(scan.Scanner):
## Determine structures and fix jumps for 2.3+
self.detect_structure(i, op)
if op >= HAVE_ARGUMENT:
if self.op_hasArgument(op):
label = self.fixed_jumps.get(i)
oparg = self.get_argument(i)
if label is None:

View File

@@ -194,6 +194,12 @@ class Scanner27(scan.Scanner):
print >>out
return rv, customize
def op_size(self, op):
if op < self.opc.HAVE_ARGUMENT:
return 1
else:
return 3
def build_stmt_indices(self):
code = self.code
start = 0;