Skip to content

Commit 342c071

Browse files
committed
Done splitting cfg into three
cfg -> interprocedural_cfg, intraprocedural_cfg and base_cfg
1 parent c7b3b87 commit 342c071

File tree

4 files changed

+708
-686
lines changed

4 files changed

+708
-686
lines changed

pyt/base_cfg.py

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,312 @@ def handle_or_else(self, orelse, test):
318318
else_connect_statements = self.stmt_star_handler(orelse)
319319
test.connect(else_connect_statements.first_statement)
320320
return else_connect_statements.last_statements
321+
322+
def remove_breaks(self, last_statements):
323+
"""Remove all break statements in last_statements."""
324+
return [n for n in last_statements if type(n) is not BreakNode]
325+
326+
def visit_If(self, node):
327+
label_visitor = LabelVisitor()
328+
label_visitor.visit(node.test)
329+
330+
test = self.append_node(Node(label_visitor.result, node, line_number = node.lineno, path=self.filenames[-1]))
331+
332+
self.add_if_label(test)
333+
334+
body_connect_stmts = self.stmt_star_handler(node.body)
335+
if isinstance(body_connect_stmts, IgnoredNode):
336+
body_connect_stmts = ConnectStatements(first_statement=test, last_statements=[], break_statements=[])
337+
test.connect(body_connect_stmts.first_statement)
338+
339+
if node.orelse:
340+
orelse_last_nodes = self.handle_or_else(node.orelse, test)
341+
body_connect_stmts.last_statements.extend(orelse_last_nodes)
342+
else:
343+
body_connect_stmts.last_statements.append(test) # if there is no orelse, test needs an edge to the next_node
344+
345+
last_statements = self.remove_breaks(body_connect_stmts.last_statements)
346+
347+
return ControlFlowNode(test, last_statements, break_statements=body_connect_stmts.break_statements)
348+
349+
def visit_NameConstant(self, node):
350+
label_visitor = LabelVisitor()
351+
label_visitor.visit(node)
352+
353+
return self.append_node(Node(label_visitor.result, node.__class__.__name__, node, line_number=node.lineno, path=self.filenames[-1]))
354+
355+
def visit_Raise(self, node):
356+
label = LabelVisitor()
357+
label.visit(node)
358+
359+
return self.append_node(RaiseNode(label.result, node, line_number=node.lineno, path=self.filenames[-1]))
360+
361+
def handle_stmt_star_ignore_node(self, body, fallback_cfg_node):
362+
try:
363+
fallback_cfg_node.connect(body.first_statement)
364+
except AttributeError:
365+
body = ConnectStatements([fallback_cfg_node], [fallback_cfg_node], list())
366+
return body
367+
368+
def visit_Try(self, node):
369+
try_node = self.append_node(Node('Try', node, line_number=node.lineno, path=self.filenames[-1]))
370+
371+
body = self.stmt_star_handler(node.body)
372+
body = self.handle_stmt_star_ignore_node(body, try_node)
373+
374+
last_statements = list()
375+
for handler in node.handlers:
376+
try:
377+
name = handler.type.id
378+
except AttributeError:
379+
name = ''
380+
handler_node = self.append_node(Node('except ' + name + ':', handler, line_number=handler.lineno, path=self.filenames[-1]))
381+
for body_node in body.last_statements:
382+
body_node.connect(handler_node)
383+
handler_body = self.stmt_star_handler(handler.body)
384+
handler_body = self.handle_stmt_star_ignore_node(handler_body, handler_node)
385+
last_statements.extend(handler_body.last_statements)
386+
387+
if node.orelse:
388+
orelse_last_nodes = self.handle_or_else(node.orelse, body.last_statements[-1])
389+
body.last_statements.extend(orelse_last_nodes)
390+
391+
if node.finalbody:
392+
finalbody = self.stmt_star_handler(node.finalbody)
393+
for last in last_statements:
394+
last.connect(finalbody.first_statement)
395+
396+
for last in body.last_statements:
397+
last.connect(finalbody.first_statement)
398+
399+
body.last_statements.extend(finalbody.last_statements)
400+
401+
last_statements.extend(self.remove_breaks(body.last_statements))
402+
403+
return ControlFlowNode(try_node, last_statements, break_statements=body.break_statements)
404+
405+
def get_names(self, node, result):
406+
"""Recursively finds all names."""
407+
if isinstance(node, ast.Name):
408+
return node.id + result
409+
elif isinstance(node, ast.Subscript):
410+
return result
411+
else:
412+
return self.get_names(node.value, result + '.' + node.attr)
413+
414+
def extract_left_hand_side(self, target):
415+
"""Extract the left hand side varialbe from a target.
416+
417+
Removes list indexes, stars and other left hand side elements.
418+
"""
419+
left_hand_side = self.get_names(target, '')
420+
421+
left_hand_side.replace('*', '')
422+
if '[' in left_hand_side:
423+
index = left_hand_side.index('[')
424+
left_hand_side = target[0:index]
425+
426+
return left_hand_side
427+
428+
def assign_tuple_target(self, node, right_hand_side_variables):
429+
new_assignment_nodes = list()
430+
for i, target in enumerate(node.targets[0].elts):
431+
value = node.value.elts[i]
432+
433+
label = LabelVisitor()
434+
label.visit(target)
435+
436+
if isinstance(value, ast.Call):
437+
new_ast_node = ast.Assign(target, value)
438+
new_ast_node.lineno = node.lineno
439+
440+
new_assignment_nodes.append( self.assignment_call_node(label.result, new_ast_node))
441+
442+
else:
443+
label.result += ' = '
444+
label.visit(value)
445+
446+
new_assignment_nodes.append(self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(target), ast.Assign(target, value), right_hand_side_variables, line_number = node.lineno, path=self.filenames[-1])))
447+
448+
449+
self.connect_nodes(new_assignment_nodes)
450+
return ControlFlowNode(new_assignment_nodes[0], [new_assignment_nodes[-1]], []) # return the last added node
451+
452+
def assign_multi_target(self, node, right_hand_side_variables):
453+
new_assignment_nodes = list()
454+
455+
for target in node.targets:
456+
label = LabelVisitor()
457+
label.visit(target)
458+
left_hand_side = label.result
459+
label.result += ' = '
460+
label.visit(node.value)
461+
462+
new_assignment_nodes.append(self.append_node(AssignmentNode(label.result, left_hand_side, ast.Assign(target, node.value), right_hand_side_variables, line_number = node.lineno, path=self.filenames[-1])))
463+
464+
self.connect_nodes(new_assignment_nodes)
465+
return ControlFlowNode(new_assignment_nodes[0], [new_assignment_nodes[-1]], []) # return the last added node
466+
467+
def visit_Assign(self, node):
468+
rhs_visitor = RHSVisitor()
469+
rhs_visitor.visit(node.value)
470+
if isinstance(node.targets[0], ast.Tuple): # x,y = [1,2]
471+
if isinstance(node.value, ast.Tuple):
472+
return self.assign_tuple_target(node, rhs_visitor.result)
473+
elif isinstance(node.value, ast.Call):
474+
call = None
475+
for element in node.targets[0].elts:
476+
label = LabelVisitor()
477+
label.visit(element)
478+
call = self.assignment_call_node(label.result, node)
479+
return call
480+
elif len(node.targets) > 1: # x = y = 3
481+
return self.assign_multi_target(node, rhs_visitor.result)
482+
else:
483+
if isinstance(node.value, ast.Call): # x = call()
484+
485+
label = LabelVisitor()
486+
label.visit(node.targets[0])
487+
return self.assignment_call_node(label.result, node)
488+
else: # x = 4
489+
label = LabelVisitor()
490+
label.visit(node)
491+
return self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(node.targets[0]), node, rhs_visitor.result, line_number = node.lineno, path=self.filenames[-1]))
492+
493+
def assignment_call_node(self, left_hand_label, ast_node):
494+
"""Handle assignments that contain a function call on its right side."""
495+
self.undecided = True # Used for handling functions in assignments
496+
497+
rhs_visitor = RHSVisitor()
498+
rhs_visitor.visit(ast_node.value)
499+
500+
call = self.visit(ast_node.value)
501+
502+
call_label = ''
503+
call_assignment = None
504+
if isinstance(call, AssignmentNode): # assignment after returned nonbuiltin
505+
call_label = call.left_hand_side
506+
call_assignment = AssignmentNode(left_hand_label + ' = ' + call_label, left_hand_label, ast_node, [call.left_hand_side], line_number=ast_node.lineno, path=self.filenames[-1])
507+
call.connect(call_assignment)
508+
else: # assignment to builtin
509+
call_label = call.label
510+
call_assignment = AssignmentNode(left_hand_label + ' = ' + call_label, left_hand_label, ast_node, rhs_visitor.result, line_number=ast_node.lineno, path=self.filenames[-1])
511+
512+
self.nodes.append(call_assignment)
513+
514+
self.undecided = False
515+
516+
return call_assignment
517+
518+
def visit_AugAssign(self, node):
519+
label = LabelVisitor()
520+
label.visit(node)
521+
522+
rhs_visitor = RHSVisitor()
523+
rhs_visitor.visit(node.value)
524+
525+
return self.append_node(AssignmentNode(label.result, self.extract_left_hand_side(node.target), node, rhs_visitor.result, line_number = node.lineno, path=self.filenames[-1]))
526+
527+
def loop_node_skeleton(self, test, node):
528+
"""Common handling of looped structures, while and for."""
529+
body_connect_stmts = self.stmt_star_handler(node.body)
530+
531+
test.connect(body_connect_stmts.first_statement)
532+
test.connect_predecessors(body_connect_stmts.last_statements)
533+
534+
# last_nodes is used for making connections to the next node in the parent node
535+
# this is handled in stmt_star_handler
536+
last_nodes = list()
537+
last_nodes.extend(body_connect_stmts.break_statements)
538+
539+
if node.orelse:
540+
orelse_connect_stmts = self.stmt_star_handler(node.orelse)
541+
542+
test.connect(orelse_connect_stmts.first_statement)
543+
last_nodes.extend(orelse_connect_stmts.last_statements)
544+
else:
545+
last_nodes.append(test) # if there is no orelse, test needs an edge to the next_node
546+
547+
return ControlFlowNode(test, last_nodes, list())
548+
549+
def add_while_label(self, node):
550+
"""Prepend 'while' and append ':' to the label of a node."""
551+
node.label = 'while ' + node.label + ':'
552+
553+
def visit_While(self, node):
554+
label_visitor = LabelVisitor()
555+
label_visitor.visit(node.test)
556+
557+
test = self.append_node(Node(label_visitor.result, node, line_number = node.lineno, path=self.filenames[-1]))
558+
559+
self.add_while_label(test)
560+
561+
return self.loop_node_skeleton(test, node)
562+
563+
def visit_For(self, node):
564+
self.undecided = True # Used for handling functions in for loops
565+
566+
#issue23
567+
iterator_label = LabelVisitor()
568+
iterator = iterator_label.visit(node.iter)
569+
self.undecided = False
570+
571+
target_label = LabelVisitor()
572+
target = target_label.visit(node.target)
573+
574+
for_node = self.append_node(Node("for " + target_label.result + " in " + iterator_label.result + ':', node, line_number = node.lineno, path=self.filenames[-1]))
575+
576+
577+
578+
if isinstance(node.iter, ast.Call) and get_call_names_as_string(node.iter.func) in self.function_names:
579+
last_node = self.visit(node.iter)
580+
last_node.connect(for_node)
581+
582+
583+
return self.loop_node_skeleton(for_node, node)
584+
585+
def visit_Expr(self, node):
586+
return self.visit(node.value)
587+
588+
def add_builtin(self, node):
589+
label = LabelVisitor()
590+
label.visit(node)
591+
builtin_call = Node(label.result, node, line_number = node.lineno, path=self.filenames[-1])
592+
593+
if not self.undecided:
594+
self.nodes.append(builtin_call)
595+
self.undecided = False
596+
return builtin_call
597+
598+
def visit_Name(self, node):
599+
label = LabelVisitor()
600+
label.visit(node)
601+
602+
return self.append_node(Node(label.result, node, line_number = node.lineno, path=self.filenames[-1]))
603+
604+
def visit_With(self, node):
605+
label_visitor = LabelVisitor()
606+
label_visitor.visit(node.items[0])
607+
608+
with_node = self.append_node(Node(label_visitor.result, node, line_number=node.lineno, path=self.filenames[-1]))
609+
connect_statements = self.stmt_star_handler(node.body)
610+
with_node.connect(connect_statements.first_statement)
611+
return ControlFlowNode(with_node, connect_statements.last_statements, connect_statements.break_statements)
612+
613+
def visit_Str(self, node):
614+
return IgnoredNode()
615+
616+
def visit_Break(self, node):
617+
return self.append_node(BreakNode(node, line_number = node.lineno, path=self.filenames[-1]))
618+
619+
def visit_Pass(self, node):
620+
return self.append_node(Node('pass', node, line_number = node.lineno, path=self.filenames[-1]))
621+
622+
def visit_Continue(self, node):
623+
return self.append_node(Node('continue', node, line_number = node.lineno, path=self.filenames[-1]))
624+
625+
def visit_Delete(self, node):
626+
labelVisitor = LabelVisitor()
627+
for expr in node.targets:
628+
labelVisitor.visit(expr)
629+
return self.append_node(Node('del ' + labelVisitor.result, node, line_number = node.lineno, path=self.filenames[-1]))

0 commit comments

Comments
 (0)