def visit_printf(self, node, args): ''' printf function call ''' # Extract format and args if len(args) == 0: self.addwarn("'printf' with zero args at line %s" % ( node.coord.line,)) fmt = Const('?', line=node.coord.line) else: if isinstance(args[0], Const): fmt = args[0] args = args[1:] else: self.addwarn("First argument of 'printf' at lines %s should \ be a format" % (node.coord.line,)) fmt = Const('?', line=node.coord.line) fmt.value = fmt.value.replace('%lf', '%f') fmt.value = fmt.value.replace('%ld', '%d') fmt.value = fmt.value.replace('%lld', '%d') expr = Op('StrAppend', Var(VAR_OUT), Op('StrFormat', fmt, *args, line=node.coord.line), line=node.coord.line) self.addexpr(VAR_OUT, expr)
def visit_AugAssign(self, node): # Aug assign to a name if isinstance(node.target, ast.Name): target = Var(node.target.id) self.addtype(node.target.id, '*') value = self.visit_expr(node.value) rhs = Op(node.op.__class__.__name__, target, value, line=node.lineno) self.addexpr(target.name, rhs) # Aug assign to a index elif isinstance(node.target, ast.Subscript): if isinstance(node.target.slice, ast.Index): var = Var(node.target.value.id) right = self.visit_expr(node.value) index = self.visit_expr(node.target.slice.value) rhs = Op(node.op.__class__.__name__, self.visit(node.target), right, line=node.lineno) self.addexpr( var.name, Op('AssignElement', var, index, rhs, line=node.lineno)) else: raise NotSupported( 'Subscript assignments only allowed to Indices', line=node.lineno) else: raise NotSupported('Assignments to {} not supported'.format( node.target.__class__.__name__), line=node.lineno)
def visit_FuncCall(self, node): ''' FuncCall Attrs: name, args ''' # Get (and check) name name = self.visit_expr(node.name) if not isinstance(name, Var): raise NotSupported("Non-var function name: '%s'" % (name,), line=name.line) # Parse args args = self.visit(node.args) or [] # Special cases (scanf & printf) if name.name == 'scanf': return self.visit_scanf(node, args) elif name.name == 'printf': return self.visit_printf(node, args) # Program functions elif name.name in self.fncs: return Op('FuncCall', name, *args, line=node.coord.line) # Library functions elif name.name in self.LIB_FNCS: return Op(name.name, *args, line=node.coord.line) else: raise NotSupported( "Unsupported function call: '%s'" % (name.name,), line=node.coord.line)
def getretcond(self, expr): if isinstance(expr, Op) and expr.name == 'ite': icond = expr.args[0] ct = self.getretcond(expr.args[1]) cf = self.getretcond(expr.args[2]) cond = [] if ct is None and cf is None: return None if ct is True and cf is True: return True if ct: if ct is True: cond.append(icond.copy()) else: cond.append(Op(self.ANDOP, icond.copy(), ct.copy())) if cf: nicond = Op(self.NOTOP, icond) if cf is True: cond.append(nicond.copy()) else: cond.append(Op(self.ANDOP, nicond.copy(), cf.copy())) if len(cond) == 1: return cond[0] else: return Op(self.OROP, cond[0], cond[1]) elif isinstance(expr, Var) and expr.name == VAR_RET: return None else: return True
def visit_UnaryOp(self, node): ''' UnaryOp - unary operation Attrs: op, expr ''' expr = self.visit_expr(node.expr) # Special cases # ++/-- if node.op in ['++', '--']: if not isinstance(expr, Var): raise NotSupported('++/-- supported only for Vars', line=node.coord.line) self.addexpr(expr.name, Op(node.op[1], expr.copy(), Const('1'), line=node.coord.line)) return expr elif node.op in ['p++', 'p--']: if not isinstance(expr, Var): raise NotSupported('p++/p-- supported only for Vars', line=node.coord.line) self.addexpr(expr.name, Op(node.op[1], expr.copy(), Const('1'), line=node.coord.line)) self.postincdec += 1 return expr return Op(node.op, expr, line=node.coord.line)
def rmtmp(self, fnc): ''' Removes (merges) "tmp" or SSA-generated assignments ''' for loc in fnc.locs(): m = {} exprs = [] primed = set([]) lastret = None # Remember "real" vars and replace temps for var, expr in fnc.exprs(loc): expr.prime(primed) for v, e in m.items(): expr = expr.replace(v, e) if var.endswith('&'): m[var] = expr else: if var == VAR_RET: lastret = len(exprs) exprs.append((var, expr)) primed.add(var) # "Merge" return stmts nexprs = [] retexpr = None retcond = None for i, (var, expr) in enumerate(exprs): if var == VAR_RET: tmpretcond = self.getretcond(expr) if tmpretcond is True or retcond is None: retcond = tmpretcond elif tmpretcond is not None and retcond is not True: retcond = Op(self.OROP, retcond, tmpretcond) if retexpr: retexpr = retexpr.replace(VAR_RET, expr) else: retexpr = expr if i == lastret: nexprs.append((var, retexpr)) else: if retcond is True: continue elif retcond: expr = Op('ite', Op(self.NOTOP, retcond), expr, Var(var)) nexprs.append((var, expr)) fnc.replaceexprs(loc, nexprs)
def visit_BoolOp(self, node): func = node.op.__class__.__name__ val_model = map(self.visit_expr, node.values) expr = Op(func, val_model[0], val_model[1], line=val_model[1].line) for v in val_model[2:]: expr = Op(func, expr, v, line=v.line) return expr
def visit_Assign(self, node): if len(node.targets) != 1: raise NotSupported('Only single assignments allowed') target = node.targets[0] # Assignment to a variable if isinstance(target, ast.Name): right = self.visit_expr(node.value) self.addtype(target.id, '*') self.addexpr(target.id, right) # Assignment to indexed element elif isinstance(target, ast.Subscript): if isinstance(target.slice, ast.Index): var = Var(target.value.id) self.addtype(target.value.id, '*') right = self.visit_expr(node.value) index = self.visit_expr(target.slice.value) self.addexpr( target.value.id, Op('AssignElement', var, index, right, line=right.line)) else: raise NotSupported( 'Subscript assignments only allowed to Indices', line=node.lineno) # Assignment to a tuple elif isinstance(target, ast.Tuple): targets = map(self.visit_expr, target.elts) right = self.visit_expr(node.value) for i, target in enumerate(targets): expr = Op('GetElement', right.copy(), Const(str(i)), line=right.line) if isinstance(target, Var): self.addexpr(target.name, expr) else: raise NotSupported("Tuple non-var assignment", line=target.line) else: raise NotSupported('Assignments to {} not supported'.format( target.__class__.__name__), line=node.lineno)
def visit_Assignment(self, node): ''' Assignment Attrs: op, lvalue, rvalue ''' lvalue = self.visit_expr(node.lvalue) postincdec = self.postincdec self.postincdec = 0 rvalue = self.visit(node.rvalue) postincdec, self.postincdec = self.postincdec, postincdec if not rvalue: rvalue = Const('?', line=node.coord.line) # Cases of assignment operator if node.op == '=': pass elif len(node.op) == 2 and node.op[1] == '=': rvalue = Op(node.op[0], lvalue.copy(), rvalue, line=rvalue.line) else: raise NotSupported("Assignment operator: '%s'" % (node.op,), line=node.coord.line) # Distinguish lvalue (ID and Array) if isinstance(lvalue, Var): lval = lvalue elif (isinstance(lvalue, Op) and lvalue.name == '[]'and isinstance(lvalue.args[0], Var)): rvalue = Op('ArrayAssign', lvalue.args[0].copy(), lvalue.args[1].copy(), rvalue, line=node.coord.line) lval = lvalue.args[0] else: raise NotSupported("Assignment lvalue '%s'" % (lvalue,), line=node.coord.line) # List of expression if isinstance(rvalue, list): rvalue = rvalue[-1] # Special case when previous assignment was p++/p-- # push this assignment before the previous one self.addexpr(lval.name, rvalue.copy(), idx=-postincdec if postincdec else None) return lvalue
def visit_Decl(self, node): ''' Decl - Declaration Attrs: name, quals, storage, funcspec, type, init, bitsize (using only: name, type & init) ''' (name, type, dim) = self.visit(node.type) init = self.visit_expr(node.init, allownone=True) if not self.fncdef: try: self.addtype(name, type) except AssertionError: self.addwarn("Ignored global definition '%s' on line %s." % ( name, node.coord.line,)) return if init and dim: raise NotSupported("Array Init & Create together", line=node.coord.line) if init: self.addexpr(name, init) if dim: self.addexpr(name, Op('ArrayCreate', dim, line=dim.line)) return (name, type, dim)
def visit_InitList(self, node): ''' Array Initialization List Attrs: exprs ''' exprs = map(self.visit_expr, node.exprs or []) return Op('ArrayInit', *exprs, line=node.coord.line)
def visit_Print(self, node): ''' Only used in Python 2.x, ignores destination and newline ''' values_model = map(self.visit_expr, node.values) expr = Op('StrAppend', Var(VAR_OUT), *values_model, line=node.lineno) self.addexpr(VAR_OUT, expr)
def visit_Cast(self, node): ''' Expression case Attrs: to_type, expr ''' tt = self.visit(node.to_type) expr = self.visit_expr(node.expr) return Op('cast', Const(tt), expr, line=node.coord.line)
def visit_BinaryOp(self, node): ''' BinaryOp - binary operation Attrs: op, left, right ''' return Op(node.op, self.visit_expr(node.left), self.visit_expr(node.right), line=node.coord.line)
def visit_Expr(self, node): ''' Expressions need to be handled depending on their type. Method calls like str.lower() can be ignored (Strings are immutable), while calls like list.append() must be handled. ''' if isinstance(node.value, ast.Call): if isinstance(node.value.func, ast.Name): self.warns.append('Ignored call to {} at line {}'.format( node.value.func.id, node.lineno)) elif isinstance(node.value.func, ast.Attribute): if node.value.func.attr in self.ATTR_FNCS: call = self.visit_expr(node.value) if isinstance(node.value.func.value, ast.Subscript): var = self.visit_expr(node.value.func.value.value) index = self.visit_expr(node.value.func.value.slice) expr = Op('AssignElement', var, index, call, line=node.lineno) if isinstance(var, Var): self.addexpr(var.name, expr) else: raise NotSupported("Non-name element assignment", line=node.lineno) elif isinstance(node.value.func.value, ast.Name): var = self.visit(node.value.func.value) if isinstance(var, Var): # Skip assignment of 'pop' function if node.value.func.attr != 'pop': self.addexpr(var.name, call) else: raise NotSupported("Non-name call", line=node.lineno) else: raise NotSupported('Call to {}'.format( node.value.func.__class__.__name__), line=node.lineno) else: self.warns.append('Ignored call to {} at line {}'.format( node.value.func.attr, node.lineno)) else: raise NotSupported('Call to {}'.format( node.value.func.__class__.__name__), line=node.lineno) elif isinstance(node.value, (ast.Num, ast.Str, ast.List, ast.Tuple, ast.Dict)): # Happily ignore these side-effect free statements pass else: self.warns.append('Ignored Expr of type {} at line {}'.format( node.value.__class__.__name__, node.lineno))
def expr_list_and(self, exprs): if len(exprs) == 0: return None else: newexpr = exprs[0] for expr in exprs[1:]: newexpr = Op('&&', newexpr, expr, line=expr.line) return newexpr
def visit_SetComp(self, node): elt = self.visit_expr(node.elt) if len(node.generators) != 1: raise NotSupported("Only one generator supported", line=node.lineno) gen = self.visit_expr(node.generators[0]) return Op('SetComp', elt, gen, line=node.lineno)
def visit_Compare(self, node): comps_model = map(self.visit, node.comparators) ops_model = map(lambda x: x.__class__.__name__, node.ops) left = self.visit_expr(node.left) right = comps_model[0] op = ops_model[0] expr = Op(op, left, right, line=right.line) left = right for op, right in zip(ops_model[1:], comps_model[1:]): expr = Op('And', expr, Op(op, left, right, line=right.line), line=right.line) left = right return expr
def visit_DictComp(self, node): key = self.visit_expr(node.key) value = self.visit_expr(node.value) if len(node.generators) != 1: raise NotSupported("Only one generator supported", line=node.lineno) gen = self.visit_expr(node.generators[0]) return Op('DictComp', key, value, gen, line=node.lineno)
def visit_ArrayRef(self, node): ''' Array reference Attrs: name, subscript ''' name = self.visit_expr(node.name) if not isinstance(name, Var): raise NotSupported("ArrayName: '%s'" % (name,)) sub = self.visit_expr(node.subscript) return Op('[]', name, sub, line=node.coord.line)
def visit_Slice(self, node): args = [] if node.lower is None: args.append(Const('None')) else: args.append(self.visit_expr(node.lower)) if node.upper is None: args.append(Const('None')) else: args.append(self.visit_expr(node.upper)) if node.step is None: args.append(Const('None')) else: args.append(self.visit_expr(node.step)) return Op('Slice', *args)
def visit_comprehension(self, node): if node.ifs is None or len(node.ifs) == 0: ifs = Const('True') elif len(node.ifs) == 1: ifs = self.visit_expr(node.ifs[0]) else: raise NotSupported("Comprehension multiple ifs") target = self.visit(node.target) if not self.islistofnames(target): raise NotSupported("Target is not a list of names") iter = self.visit(node.iter) return Op('Comp', target, iter, ifs)
def optimizeif(self, preloc, condexpr, trueloc, falseloc): ''' Optimized "simple" or "loop-less" if statement ''' # Remove unneded part of the graph self.fnc.rmtrans(preloc, True) self.loc = preloc # Keep track of assigned vars varss = set() varsl = [] mt = {} mf = {} # Add exprs from branches def addvars(loc, m): for (var, expr) in self.fnc.exprs(loc): newvar = self.ssavar(var) if var not in varss: varss.add(var) varsl.append(var) # Replace vars mapped so far for (v1, v2) in m.items(): expr = expr.replace(v1, Var(v2)) expr.original = (var, self.cnt) self.addexpr(newvar, expr) # Remember replacement m[var] = newvar addvars(trueloc, mt) if falseloc is not None: addvars(falseloc, mf) # Add condition condvar = self.ssavar('$cond') self.addexpr(condvar, condexpr.copy()) # Merge branches for var in varsl: self.addexpr( var, Op('ite', Var(condvar), Var(mt.get(var, var)), Var(mf.get(var, var))))
def visit_TernaryOp(self, node): ''' Ternary Operator node Attrs: cond, iftrue, iffalse ''' cond = self.visit_expr(node.cond) n = self.numexprs() ift = self.visit_expr(node.iftrue) iff = self.visit_expr(node.iffalse) if self.numexprs() > n: self.rmlastexprs(num=self.numexprs() - n) return self.visit_if(node, node.cond, node.iftrue, node.iffalse) return Op('ite', cond, ift, iff, line=node.coord.line)
def visit_Call(self, node): if len(node.keywords) > 0 or node.starargs or node.kwargs: raise NotSupported( 'starargs, kwargs and keyword arguments not supported', line=node.lineno) if isinstance(node.func, ast.Name): if node.func.id in self.BUILTIN_FNCS: fncname = node.func.id args = list(map(self.visit_expr, node.args)) return Op(fncname, *args, line=node.lineno) else: fnc = Var(node.func.id) args = list(map(self.visit_expr, node.args)) return Op('FuncCall', fnc, *args, line=node.lineno) elif isinstance(node.func, ast.Num): num = self.visit_expr(node.func) self.addwarn("Call to a number '%s' on line %s ignored", num, num.line) return Const('?') elif isinstance(node.func, ast.Attribute): attr = node.func.attr val = self.visit_expr(node.func.value) args = list(map(self.visit_expr, node.args)) if isinstance(val, Var) and val.name in self.MODULE_NAMES: return Op('%s_%s' % (val, attr), *args, line=node.lineno) if attr == 'pop': if isinstance(val, Var): popvar = self.ssavar('pop#') self.addexpr(popvar, Op(attr, val, *args, line=node.lineno)) self.addexpr(val.name, Op('GetElement', Var(popvar), Const('0'))) return Op('GetElement', Var(popvar), Const('1')) else: raise NotSupported('Pop to a non-name list') return Op(attr, val, *args, line=node.lineno) else: raise NotSupported('Call of {} not supported'.format( node.func.__class__.__name__), line=node.lineno)
def visit_Delete(self, node): if len(node.targets) > 1: raise NotSupported('Multiple delete targets') target = self.visit(node.targets[0]) if isinstance(target, Op): if target.name == 'GetElement': if isinstance(target.args[0], Var): if len(target.args) != 2: raise NotSupported('Delete target with %d args' % (len(target.args, ))) delexpr = Op('Delete', *target.args, line=node.lineno) self.addexpr(target.args[0].name, delexpr) else: raise NotSupported('Delete target not Var, but %s' % (target.args[0].__class__)) else: raise NotSupported('Delete target op: %s' % (target.name, )) else: raise NotSupported('Delete target: %s' % (target.__class__, ))
def visit_For(self, node): if node.orelse: raise NotSupported("For-Else not supported", line=self.getline(node.orelse)) # Iterated expression it = self.visit_expr(node.iter) # Targets of iteration if isinstance(node.target, ast.Name): targets = [self.visit_expr(node.target)] elif isinstance(node.target, ast.Tuple): targets = map(self.visit_expr, node.target.elts) else: raise NotSupported('For loop with {} as target'.format( node.target.__class__.__name__), line=node.lineno) hiddenvar = self.hiddenvarcnt self.hiddenvarcnt += 1 # Set up the iterated variable iter_name = 'iter#{}'.format(hiddenvar) it_var = Var(iter_name) self.addtype(iter_name, '*') # Set up the iteration index ind_name = 'ind#{}'.format(hiddenvar) ind_var = Var(ind_name) self.addtype(ind_name, 'int') # Add assignments to iterators self.addexpr(it_var.name, it) self.addexpr(ind_var.name, Const(str(0), line=node.lineno)) # Condition is ind_var < len(iter_var) cond = Op('Lt', ind_var.copy(), Op('len', it_var.copy()), line=node.iter.lineno) # Assignments to iterated variable(s) prebody = [] el = Op('GetElement', it_var.copy(), ind_var.copy(), line=node.target.lineno) if len(targets) == 1: prebody.append((targets[0].name, el.copy())) else: for i, t in enumerate(targets): eli = Op('GetElement', el.copy(), Const(str(i)), line=node.target.lineno) prebody.append((t.name, eli)) # Add index variable increment prebody.append((ind_var.name, Op('Add', ind_var.copy(), Const(str(1)), line=node.iter.lineno))) self.visit_loop(node, None, cond, None, node.body, False, 'for', prebody=prebody)
def visit_Subscript(self, node): val = self.visit_expr(node.value) return Op('GetElement', val, self.visit_expr(node.slice), line=node.lineno)
def visit_Set(self, node): elts = map(self.visit_expr, node.elts) return Op('SetInit', *elts, line=node.lineno)
def visit_Dict(self, node): keys = map(self.visit_expr, node.keys) vals = map(self.visit_expr, node.values) args = list(chain(*zip(keys, vals))) return Op('DictInit', *args, line=node.lineno)
def visit_For(self, node): if node.orelse: raise NotSupported("For-Else not supported", line=self.getline(node.orelse)) # Iterated expression it = self.visit_expr(node.iter) # Targets of iteration if isinstance(node.target, ast.Name): self.addtype(node.target.id, '*') targets = [self.visit_expr(node.target)] elif isinstance(node.target, ast.Tuple): for el in node.target.elts: if isinstance(el, ast.Name): self.addtype(el.id, '*') targets = map(self.visit_expr, node.target.elts) else: raise NotSupported( 'For loop with {} as target'.format( node.target.__class__.__name__), line=node.lineno) hiddenvar = self.hiddenvarcnt self.hiddenvarcnt += 1 # Set up the iterated variable iter_name = 'iter#{}'.format(hiddenvar) it_var = Var(iter_name) self.addtype(iter_name, '*') # Set up the iteration index ind_name = 'ind#{}'.format(hiddenvar) ind_var = Var(ind_name) self.addtype(ind_name, 'int') # Add assignments to iterators self.addexpr(it_var.name, it) self.addexpr(ind_var.name, Const(str(0), line=node.lineno)) # Condition is ind_var < len(iter_var) cond = Op('Lt', ind_var.copy(), Op('len', it_var.copy()), line=node.iter.lineno) # Assignments to iterated variable(s) prebody = [] el = Op('GetElement', it_var.copy(), ind_var.copy(), line=node.target.lineno) if len(targets) == 1: prebody.append((targets[0].name, el.copy())) else: for i, t in enumerate(targets): eli = Op('GetElement', el.copy(), Const(str(i)), line=node.target.lineno) prebody.append((t.name, eli)) # Add index variable increment prebody.append((ind_var.name, Op('Add', ind_var.copy(), Const(str(1)), line=node.iter.lineno))) self.visit_loop(node, None, cond, None, node.body, False, 'for', prebody=prebody)