def _generate_cfg_slice(self, slice_instrs): block_map = {} # function block number : slice block number slice_func = FunctionBlock(self.func.label) for block in self.sorted_blocks: # Get all instructions in this block that are in the slice. linenos = block.get_instruction_linenos().intersection(slice_instrs) # Create a copy of the block. if not block_map: curr_bloc = slice_func else: curr_bloc = Block() block_map[block.label] = curr_bloc # Copy instructions in the block to block copy. for lineno in linenos: instruction = block.get_instruction(lineno) curr_bloc.add_instruction(instruction) # Copy the block's successors. for successor in block.successors: if successor in block_map: curr_bloc.add_successor(block_map[successor]) # Copy the block's predecessors. for predecessor in block.predecessors: if predecessor in block_map: curr_bloc.add_predecessor(block_map[predecessor]) return slice_func
def _visit_loop(self, instr_type, conditional_nodes, conditional_lineno, body): start_block = self.current_block prev_control = self.current_control prev_guard_block = self.guard_block prev_after_block = self.after_block guard_block = Block() start_body_block = Block() after_block = Block() # Add successors/predcessors. start_block.add_successor(guard_block) guard_block.add_successor(start_body_block) guard_block.add_successor(after_block) # Add conditional to guard block. self.current_block = guard_block for node in conditional_nodes: self._visit_item(node) self._add_instruction_info(lineno=conditional_lineno, instr_type=instr_type) self.current_control = conditional_lineno # Add body to body block. self.current_block = start_body_block self.guard_block = guard_block self.after_block = after_block self._visit_item(body) self.guard_block = prev_guard_block self.after_block = prev_after_block self._add_successor(self.current_block, guard_block) self.current_control = prev_control self.current_block = after_block
def visit_If(self, node): prev_control = self.current_control start_block = self.current_block # Add conditional to current block. self._visit_item(node.test) self.current_control = node.test.lineno # Add body to if block. start_if_block = Block() start_block.add_successor(start_if_block) self.current_block = start_if_block self._visit_item(node.body) end_if_block = self.current_block # Add orelse to else block. if node.orelse: start_else_block = Block() start_block.add_successor(start_else_block) self.current_block = start_else_block # If else block then add instruction type ELSE as a placeholder. if not isinstance(node.orelse[0], _ast.If): # Gets line number of else. lineno = node.orelse[0].lineno - 1 while (lineno in self.tokens.comments or lineno in self.tokens.blank_lines): lineno -= 1 # Adds ELSE instruction. self._add_instruction_info(lineno, instr_type=InstructionType.ELSE) self.current_control = lineno self._visit_item(node.orelse) end_else_block = self.current_block else: end_else_block = start_block # Add after block if all paths don't have a return. if end_if_block or end_else_block: after_block = Block() self._add_successor(end_if_block, after_block) self._add_successor(end_else_block, after_block) self.current_block = after_block self.current_control = prev_control
def _visit_exception(self, lineno, body, handlers): prev_control = self.current_control start_block = self.current_block end_except_block = None # Add TRY statement to current block. self._add_instruction_info(lineno, instr_type=InstructionType.TRY) self.current_control = lineno # Add body to try block. start_try_block = Block() start_block.add_successor(start_try_block) self.current_block = start_try_block self._visit_item(body) end_try_block = self.current_block # Add except data. for handler in handlers: # Add body to except block. start_except_block = Block() start_block.add_successor(start_except_block) self.current_block = start_except_block # Visit handler header (ex. except Exception as e). if isinstance(handler.name, str): self._add_instruction_info(handler.lineno, var='e', action=TypeVariable.STORE) else: self._visit_item(handler.name) self._visit_item(handler.type) self._add_instruction_info(handler.lineno, instr_type=InstructionType.EXCEPT) self.current_control = handler.lineno # Visit handler body. self._visit_item(handler.body) end_except_block = self.current_block # Add after block if all paths don't have a return. if end_try_block or end_except_block: after_block = Block() self._add_successor(end_try_block, after_block) self._add_successor(end_except_block, after_block) self.current_block = after_block self.current_control = prev_control
def test_to_string(self): result = ('func1 | \n' ' | successors()\n' ' | predecessors()\n\n') self.assertEqual(str(self.func_block1), result) block = Block() self.func_block1.add_successor(block) label = block.label spaces = ' ' * len(label) result = ('func1 | \n' ' | successors({})\n' ' | predecessors()\n\n' '{} | \n' '{} | successors()\n' '{} | predecessors(func1)\n\n').format( label, label, spaces, spaces) self.assertEqual(str(self.func_block1), result)
def visit_FunctionDef(self, node): prev_block = self.current_block # Create FunctionBlock. self.func_block = self.current_block = FunctionBlock(node.name) self._add_instruction_info(node.lineno, instr_type=InstructionType.FUNCTION_HEADER) # Create exit block. self.exit_block = Block() self.block_list.add(self.current_block) # Visit function information. self.generic_visit(node) self._add_successor(self.current_block, self.exit_block) # Add blank linenos and comments. linenos = set(range(node.lineno, self.last_lineno+1)) self.func_block.blank_lines = linenos.intersection(self.tokens.blank_lines) self.func_block.comments = linenos.intersection(self.tokens.comments) self.func_block.unimportant |= self.func_block.blank_lines self.func_block.unimportant |= self.func_block.comments self.current_block = prev_block
def setUp(self): self.block1 = Block() self.block2 = Block() self.block3 = Block() self.block4 = Block()
class TestBlock(unittest.TestCase): def setUp(self): self.block1 = Block() self.block2 = Block() self.block3 = Block() self.block4 = Block() def assertSuccessorsEqual(self, block, successors): actual = set(block.successors.keys()) if successors is None: self.assertFalse(actual) else: expected = [successor.label for successor in successors] self.assertEqual(actual, set(expected)) def assertPredecessorsEqual(self, block, predecessors): actual = set(block.predecessors.keys()) if predecessors is None: self.assertFalse(actual) else: expected = [predecessor.label for predecessor in predecessors] self.assertEqual(actual, set(expected)) def test_to_string(self): instr = Instruction(lineno=1) instr.referenced.add('varA') instr.defined.add('varB') self.block1.add_instruction(instr) label = self.block1.label spaces = ' ' * len(label) result = ('{} | \n' '{} | successors()\n' '{} | predecessors()\n' '\t#1 | REF(varA) DEF(varB)\n').format( label, spaces, spaces) self.assertEqual(str(self.block1), result) def test_labels(self): block_label1 = int(self.block1.label[1:]) block_label2 = int(self.block2.label[1:]) self.assertEqual(block_label2 - block_label1, 1) # Ensure error when trying to reset label. with self.assertRaises(ValueError) as context: self.block1.label = 'error_label' def test_equals(self): self.block2.add_predecessor(self.block1) self.block3.add_predecessor(self.block1) self.block2.add_successor(self.block4) self.block3.add_successor(self.block4) self.assertFalse(self.block2 == None) self.assertFalse(self.block2 == 1) self.assertFalse(self.block2 == self.block3) self.assertTrue(self.block2.equals(self.block3)) instrA = Instruction(lineno=1) instrB = Instruction(lineno=1) instrB.referenced.add('varA') self.block2.add_instruction(instrA) self.block3.add_instruction(instrB) self.assertFalse(self.block2.equals(self.block3)) def test_check_successor_equality(self): # Checks block with less than 2 successors. self.assertTrue(self.block1.check_successor_equality()) self.block1.add_successor(self.block2) self.assertTrue(self.block1.check_successor_equality()) # Checks block with successors. self.block1.add_successor(self.block3) self.block1.add_successor(self.block4) self.assertTrue(self.block1.check_successor_equality()) # Checks block with different successors. instr = Instruction(lineno=1) self.block4.add_instruction(instr) self.assertFalse(self.block1.check_successor_equality()) def test_add_reference(self): self.block1.add_reference(lineno=1, variable='varA') self.assertEqual(self.block1._instructions[1].referenced, set(['varA'])) self.assertFalse(self.block1._instructions[1].defined) self.assertEqual(len(self.block1.get_instructions()), 1) self.assertEqual(set(self.block1._instructions.keys()), set([1])) self.block1.add_reference(lineno=2, variable='varA') self.block1.add_reference(lineno=2, variable='varB') self.assertEqual(self.block1._instructions[2].referenced, set(['varA', 'varB'])) self.assertFalse(self.block1._instructions[2].defined) self.assertEqual(len(self.block1.get_instructions()), 2) self.assertEqual(set(self.block1._instructions.keys()), set([1, 2])) def test_add_definition(self): self.block1.add_definition(lineno=1, variable='varB') self.assertFalse(self.block1._instructions[1].referenced) self.assertEqual(self.block1._instructions[1].defined, set(['varB'])) self.assertEqual(len(self.block1.get_instructions()), 1) self.assertEqual(set(self.block1._instructions.keys()), set([1])) self.block1.add_definition(lineno=2, variable='varA') self.block1.add_definition(lineno=2, variable='varC') self.assertFalse(self.block1._instructions[2].referenced) self.assertEqual(self.block1._instructions[2].defined, set(['varA', 'varC'])) self.assertEqual(len(self.block1.get_instructions()), 2) self.assertEqual(set(self.block1._instructions.keys()), set([1, 2])) def test_add_instruction_type(self): self.block1.add_reference(lineno=1, variable='varA') self.assertEqual(self.block1._instructions[1].referenced, set(['varA'])) self.assertEqual(self.block1._instructions[1].instruction_type, None) self.block1.add_instruction_type( lineno=1, instruction_type=InstructionType.RETURN) self.assertEqual(self.block1._instructions[1].instruction_type, InstructionType.RETURN) def test_add_instr_control(self): self.block1.add_reference(lineno=1, variable='varA') self.block1.add_reference(lineno=2, variable='varA') self.assertEqual(self.block1._instructions[1].referenced, set(['varA'])) self.assertEqual(self.block1._instructions[2].referenced, set(['varA'])) self.assertEqual(self.block1._instructions[2].control, None) self.block1.add_instr_control(lineno=2, control=1) self.assertEqual(self.block1._instructions[2].control, 1) def test_add_instr_indent(self): self.block1.add_reference(lineno=1, variable='varA') self.block1.add_reference(lineno=2, variable='varA') self.assertEqual(self.block1._instructions[1].referenced, set(['varA'])) self.assertEqual(self.block1._instructions[2].referenced, set(['varA'])) self.assertEqual(self.block1._instructions[2].indentation, None) self.block1.add_instr_indent(lineno=2, indentation=1) self.assertEqual(self.block1._instructions[2].indentation, 1) def test_method_add_instruction(self): instr = Instruction(lineno=1) instr.referenced.add('varA') instr.defined.add('varB') self.block1.add_instruction(instr) self.assertEqual(len(self.block1.get_instructions()), 1) self.assertEqual(set(self.block1._instructions.keys()), set([1])) def test_add_multiline_instructions(self): instr = Instruction(lineno=1) self.block1.add_instruction(instr) self.assertFalse(self.block1._instructions[1].multiline) self.block1.add_multiline_instructions(lineno=1, linenos=[2, 3]) self.assertEqual(self.block1._instructions[1].multiline, set([1, 2, 3])) def test_add_successor(self): self.block1.add_successor(self.block1) self.block1.add_successor(self.block2) self.block1.add_successor(self.block3) self.assertSuccessorsEqual(self.block1, [self.block2, self.block3]) self.assertPredecessorsEqual(self.block2, [self.block1]) self.assertPredecessorsEqual(self.block3, [self.block1]) def test_add_predecessor(self): self.block1.add_predecessor(self.block1) self.block1.add_predecessor(self.block2) self.block1.add_predecessor(self.block3) self.assertPredecessorsEqual(self.block1, [self.block2, self.block3]) self.assertSuccessorsEqual(self.block2, [self.block1]) self.assertSuccessorsEqual(self.block3, [self.block1]) def test_set_successors(self): self.block1.add_successor(self.block1) self.block1.add_successor(self.block2) self.block1.add_successor(self.block3) self.assertSuccessorsEqual(self.block1, [self.block2, self.block3]) self.assertPredecessorsEqual(self.block2, [self.block1]) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.set_successors([self.block1, self.block2]) self.assertSuccessorsEqual(self.block1, [self.block2]) self.assertPredecessorsEqual(self.block2, [self.block1]) self.assertPredecessorsEqual(self.block3, None) self.block1.set_successors([self.block3]) self.assertSuccessorsEqual(self.block1, [self.block3]) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.set_successors([]) self.assertSuccessorsEqual(self.block1, None) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, None) def test_replace_successor(self): self.block1.add_successor(self.block1) self.block1.add_successor(self.block2) self.assertSuccessorsEqual(self.block1, [self.block2]) self.assertPredecessorsEqual(self.block2, [self.block1]) self.assertPredecessorsEqual(self.block3, None) self.block1.replace_successor(self.block2, self.block3) self.assertSuccessorsEqual(self.block1, [self.block3]) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.replace_successor(self.block3, self.block1) self.assertSuccessorsEqual(self.block1, None) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, None) # Ensure error when trying to replace non-existant successor. with self.assertRaises(ValueError) as context: self.block1.replace_successor(self.block3, self.block1) # Before: Block 1 --> Block2 --> Block3 # After: Block1 --> Block3 and Block2 --> Block3 self.block1.add_successor(self.block2) self.block2.add_successor(self.block3) self.block1.replace_successor(self.block2, self.block3) self.assertSuccessorsEqual(self.block1, [self.block3]) self.assertPredecessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block2, [self.block3]) self.assertPredecessorsEqual(self.block3, [self.block1, self.block2]) def test_replace_predecessor(self): self.block1.add_predecessor(self.block1) self.block1.add_predecessor(self.block2) self.assertPredecessorsEqual(self.block1, [self.block2]) self.assertSuccessorsEqual(self.block2, [self.block1]) self.assertSuccessorsEqual(self.block3, None) self.block1.replace_predecessor(self.block2, self.block3) self.assertPredecessorsEqual(self.block1, [self.block3]) self.assertSuccessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block3, [self.block1]) self.block1.replace_predecessor(self.block3, self.block1) self.assertPredecessorsEqual(self.block1, None) self.assertSuccessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block3, None) # Ensure error when trying to replace non-existant predecessor. with self.assertRaises(ValueError) as context: self.block1.replace_predecessor(self.block3, self.block1) # Before: Block 1 <-- Block2 <-- Block3 # After: Block1 <-- Block3 and Block2 <-- Block3 self.block1.add_predecessor(self.block2) self.block2.add_predecessor(self.block3) self.block1.replace_predecessor(self.block2, self.block3) self.assertPredecessorsEqual(self.block1, [self.block3]) self.assertSuccessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block2, [self.block3]) self.assertSuccessorsEqual(self.block3, [self.block1, self.block2]) def test_remove_successor(self): self.block1.add_successor(self.block1) self.block1.add_successor(self.block2) self.block1.add_successor(self.block3) self.assertSuccessorsEqual(self.block1, [self.block2, self.block3]) self.assertPredecessorsEqual(self.block2, [self.block1]) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.remove_successor(self.block2) self.assertSuccessorsEqual(self.block1, [self.block3]) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.remove_successor(self.block1) self.assertSuccessorsEqual(self.block1, [self.block3]) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, [self.block1]) self.block1.remove_successor(self.block3) self.assertSuccessorsEqual(self.block1, None) self.assertPredecessorsEqual(self.block2, None) self.assertPredecessorsEqual(self.block3, None) def test_remove_predecessor(self): self.block1.add_predecessor(self.block1) self.block1.add_predecessor(self.block2) self.block1.add_predecessor(self.block3) self.assertPredecessorsEqual(self.block1, [self.block2, self.block3]) self.assertSuccessorsEqual(self.block2, [self.block1]) self.assertSuccessorsEqual(self.block3, [self.block1]) self.block1.remove_predecessor(self.block2) self.assertPredecessorsEqual(self.block1, [self.block3]) self.assertSuccessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block3, [self.block1]) self.block1.remove_predecessor(self.block1) self.assertPredecessorsEqual(self.block1, [self.block3]) self.assertSuccessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block3, [self.block1]) self.block1.remove_predecessor(self.block3) self.assertPredecessorsEqual(self.block1, None) self.assertSuccessorsEqual(self.block2, None) self.assertSuccessorsEqual(self.block3, None) def test_destroy(self): self.block1.set_successors([self.block2, self.block3]) self.block2.add_successor(self.block4) self.block3.add_successor(self.block4) self.block3.destroy() self.assertSuccessorsEqual(self.block1, [self.block2]) self.assertPredecessorsEqual(self.block3, None) self.assertSuccessorsEqual(self.block3, None) self.assertPredecessorsEqual(self.block4, [self.block2]) self.block2.destroy() self.assertSuccessorsEqual(self.block1, None) self.assertPredecessorsEqual(self.block3, None) self.assertSuccessorsEqual(self.block3, None) self.assertPredecessorsEqual(self.block4, None) def test_get_instruction(self): instr = Instruction(lineno=1) instr.referenced.add('varA') instr.defined.add('varB') self.block1.add_instruction(instr) result = self.block1.get_instruction(lineno=1) self.assertEqual(result.lineno, instr.lineno) self.assertEqual(result.referenced, instr.referenced) self.assertEqual(result.defined, instr.defined) self.assertFalse(self.block1.get_instruction(lineno=2)) def test_get_instruction_linenos(self): self.assertFalse(self.block1.get_instruction_linenos()) self.block1.add_definition(lineno=1, variable='varB') self.block1.add_definition(lineno=2, variable='varA') self.assertEqual(self.block1.get_instruction_linenos(), set([1, 2])) def test_get_instructions(self): self.block1.add_reference(lineno=2, variable='varA') self.block1.add_definition(lineno=1, variable='varA') instructions = self.block1.get_instructions() self.assertEqual(instructions[0].lineno, 1) self.assertEqual(instructions[1].lineno, 2) def test_get_last_instruction(self): self.assertFalse(self.block1.get_instruction_linenos()) self.block1.add_definition(lineno=1, variable='varB') self.block1.add_definition(lineno=2, variable='varA') self.assertEqual(self.block1.get_last_instruction(), 2) def test_get_first_successor(self): self.block1.set_successors([self.block2, self.block3]) successor = self.block1.get_first_successor() self.assertEqual(successor, self.block2) self.block1.remove_successor(successor) successor = self.block1.get_first_successor() self.assertEqual(successor, self.block3) self.block1.remove_successor(successor) successor = self.block1.get_first_successor() self.assertEqual(successor, None) def test_get_first_predecessor(self): self.block1.add_predecessor(self.block2) self.block1.add_predecessor(self.block3) predecessor = self.block1.get_first_predecessor() self.assertEqual(predecessor, self.block2) self.block1.remove_predecessor(predecessor) predecessor = self.block1.get_first_predecessor() self.assertEqual(predecessor, self.block3) self.block1.remove_predecessor(predecessor) predecessor = self.block1.get_first_predecessor() self.assertEqual(predecessor, None)