Attempt at a fix for issue #48.
This commit is contained in:
parent
7e7b362e47
commit
ffc067f852
6 changed files with 132 additions and 44 deletions
|
|
@ -5,13 +5,13 @@ import opcode
|
|||
import opcode_util
|
||||
import pprint
|
||||
|
||||
from bytecode_visitor import BytecodeFlowVisitor, BenignBytecodeVisitorMixin
|
||||
from bytecode_visitor import BasicBlockVisitor, BenignBytecodeVisitorMixin
|
||||
from control_flow import ControlFlowGraph
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
class ControlFlowBuilder (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
||||
'''Visitor responsible for traversing a bytecode flow object and
|
||||
class ControlFlowBuilder (BenignBytecodeVisitorMixin, BasicBlockVisitor):
|
||||
'''Visitor responsible for traversing a bytecode basic block map and
|
||||
building a control flow graph (CFG).
|
||||
|
||||
The primary purpose of this transformation is to create a CFG,
|
||||
|
|
@ -27,21 +27,26 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
|||
del self.nargs
|
||||
return ret_val
|
||||
|
||||
def enter_flow_object (self, flow):
|
||||
super(ControlFlowBuilder, self).enter_flow_object(flow)
|
||||
self.flow = flow
|
||||
def enter_blocks (self, blocks):
|
||||
super(ControlFlowBuilder, self).enter_blocks(blocks)
|
||||
self.blocks = blocks
|
||||
self.block_list = list(blocks.keys())
|
||||
self.block_list.sort()
|
||||
self.cfg = ControlFlowGraph()
|
||||
for block in flow.keys():
|
||||
self.cfg.add_block(block, flow[block])
|
||||
self.loop_stack = []
|
||||
for block in self.block_list:
|
||||
self.cfg.add_block(block, blocks[block])
|
||||
|
||||
def exit_flow_object (self, flow):
|
||||
super(ControlFlowBuilder, self).exit_flow_object(flow)
|
||||
assert self.flow == flow
|
||||
def exit_blocks (self, blocks):
|
||||
super(ControlFlowBuilder, self).exit_blocks(blocks)
|
||||
assert self.blocks == blocks
|
||||
self.cfg.compute_dataflow()
|
||||
self.cfg.update_for_ssa()
|
||||
ret_val = self.cfg
|
||||
del self.loop_stack
|
||||
del self.cfg
|
||||
del self.flow
|
||||
del self.block_list
|
||||
del self.blocks
|
||||
return ret_val
|
||||
|
||||
def enter_block (self, block):
|
||||
|
|
@ -58,13 +63,15 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
|||
def exit_block (self, block):
|
||||
assert block == self.block
|
||||
del self.block
|
||||
i, op, opname, arg, args = self.flow[block][-1]
|
||||
i, op, arg = self.blocks[block][-1]
|
||||
opname = opcode.opname[op]
|
||||
if op in opcode.hasjabs:
|
||||
self.cfg.add_edge(block, arg)
|
||||
elif op in opcode.hasjrel:
|
||||
self.cfg.add_edge(block, i + arg + 3)
|
||||
elif opname == 'BREAK_LOOP':
|
||||
self.cfg.add_edge(block, arg)
|
||||
loop_i, _, loop_arg = self.loop_stack[-1]
|
||||
self.cfg.add_edge(block, loop_i + loop_arg + 3)
|
||||
elif opname != 'RETURN_VALUE':
|
||||
self.cfg.add_edge(block, self._get_next_block(block))
|
||||
if op in opcode_util.hascbranch:
|
||||
|
|
@ -80,15 +87,24 @@ class ControlFlowBuilder (BenignBytecodeVisitorMixin, BytecodeFlowVisitor):
|
|||
return super(ControlFlowBuilder, self).op_STORE_FAST(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
def op_SETUP_LOOP (self, i, op, arg, *args, **kws):
|
||||
self.loop_stack.append((i, op, arg))
|
||||
return super(ControlFlowBuilder, self).op_SETUP_LOOP(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
def op_POP_BLOCK (self, i, op, arg, *args, **kws):
|
||||
self.loop_stack.pop()
|
||||
return super(ControlFlowBuilder, self).op_POP_BLOCK(i, op, arg, *args,
|
||||
**kws)
|
||||
|
||||
# ______________________________________________________________________
|
||||
|
||||
def build_cfg (func):
|
||||
'''Given a Python function, create a bytecode flow, visit the flow
|
||||
object, and return a control flow graph.'''
|
||||
import byte_flow
|
||||
return ControlFlowBuilder().visit(
|
||||
byte_flow.build_flow(func),
|
||||
opcode_util.get_code_object(func).co_argcount)
|
||||
co_obj = opcode_util.get_code_object(func)
|
||||
return ControlFlowBuilder().visit(opcode_util.build_basic_blocks(co_obj),
|
||||
co_obj.co_argcount)
|
||||
|
||||
# ______________________________________________________________________
|
||||
# Main (self-test) routine
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue