def extract_eq(expr): kind = expr.decl().kind() children = expr.children() if kind == z3.Z3_OP_EQ: lhs = children[0] rhs = children[1] if z3.is_var(lhs): return z3.get_var_index(lhs), rhs elif z3.is_var(rhs): return z3.get_var_index(rhs), lhs else: return None return None
def translate(self, expr, bound_variables=[]): if z3.is_const(expr): return self.mk_const(expr) # raise Z3_Unexpected_Expression('Unrecognized constant') elif z3.is_var(expr): # a de Bruijn indexed bound variable bv_length = len(bound_variables) return bound_variables[bv_length - z3.get_var_index(expr) - 1] elif z3.is_app(expr): args = [self.translate(expr.arg(i), bound_variables) for i in range(expr.num_args())] return self.mk_fun(expr.decl())(*args) # else: # raise Z3_Unexpected_Expression(expr) elif z3.is_quantifier(expr): num_vars = expr.num_vars() # vars = [language.const_dict[expr.var_name(i)] # for i in range(num_vars)] vars = [const(expr.var_name(i), self.mk_sort(expr.var_sort(i))) \ for i in range(num_vars)] new_bound_variables = bound_variables + vars body = self.translate(expr.body(), new_bound_variables) if expr.is_forall(): return forall(vars, body) else: return exists(vars, body) elif z3.is_func_decl(expr): return self.mk_fun(expr) else: print expr.kind raise Z3_Unexpected_Expression(expr)
def extract_equal(eq): """Transform equals in a triple: var index, value, mask""" if isinstance(eq.children()[0], z3.BitVecNumRef): rhs = eq.children()[0] lhs = eq.children()[1] else: rhs = eq.children()[1] lhs = eq.children()[0] if z3.is_var(lhs): return ResultItem( var=z3.get_var_index(lhs), value=rhs, mask=None) else: kind = lhs.decl().kind() if kind == z3.Z3_OP_EXTRACT: [high, low] = lhs.params() sort = lhs.children()[0].sort() val = rhs.as_long() << low mask = (1 << (high + 1)) - (1 << low) return ResultItem( var=z3.get_var_index(lhs.children()[0]), value=z3.BitVecVal(val, sort), mask=z3.BitVecVal(mask, sort)) else: raise base.Z3NotWellFormed( "Bad lhs for equal {}".format(eq))
def partial_leaf_substitution(expr, substitution_dict): """Replaces consts/vars in `expr` according to `substitution_dict`. If a const/var is not in `substitution_dict.keys()`, it remains unchanged. >>> a, b, c = sl.list.locs("a b c") >>> subst = {b : c} >>> partial_leaf_substitution(sl.sepcon(sl.list("a"), sl.list("b")), subst) sl.sepcon(sl.list(a), sl.list(c)) >>> i, j = Ints("i j") >>> subst = {sl.alpha : i, sl.beta : j} >>> partial_leaf_substitution(sl.alpha < sl.beta, subst) i < j """ if z3.is_const(expr) or z3.is_var(expr): return substitution_dict.get(expr, expr) elif z3.is_app(expr): new_args = [ partial_leaf_substitution(child, substitution_dict) for child in expr.children() ] return replace_args(expr, new_args) else: assert (z3.is_quantifier(expr)) new_arg = partial_leaf_substitution(expr.body(), substitution_dict) return replace_args(expr, new_arg)
def convert(t, values): if z3.is_int_value(t): return t.as_long() if z3.is_app(t): func = globals()[t.decl().name()] return func( *[convert(t.arg(i), values) for i in range(t.num_args())]) elif z3.is_var(t): return values[z3.get_var_index(t)]
def pp_expr(self, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: return self.pp_ellipses() if z3.is_app(a): return self.pp_app(a, d, xs) elif z3.is_quantifier(a): return self.pp_quantifier(a, d, xs) elif z3.is_var(a): return self.pp_var(a, d, xs) else: return to_format(self.pp_unknown())
def tagged_conditional_rewrite(s, rewriting_dict, leaf_fn, default_fn): """ Apply given rewriter to subexpressions of s bottom up """ todo = [] todo.append(s) cache = {} while todo: n = todo[len(todo) - 1] #print("Rewriting {}".format(n)) #print("Current cache: {}".format(cache)) if z3.is_var(n): # No rewriting for variables todo.pop() # TODO: This is one difference => Could be added as leaf_fn to standard rewrite as well cache[n] = leaf_fn(n) elif z3.is_app(n): # Add non-rewritten children to rewriting stack processed_all_children = True for arg in n.children(): if arg not in cache: todo.append(arg) processed_all_children = False # All children haven been rewritten, so now rewrite n itself if processed_all_children: todo.pop() new_args = [cache[arg] for arg in n.children()] enabled_rewriter = rewriting_dict.get(n.decl()) if enabled_rewriter is not None: cache[n] = enabled_rewriter(n, new_args) else: #cache[n] = replace_args(n, new_args) cache[n] = default_fn(n, new_args) else: assert (z3.is_quantifier(n)) b = n.body() if b in cache: # The argument of the quantifier has already been rewritten # Substitute this rewritten term todo.pop() # TODO: Another difference (but actually not even relevant in our setting). This could also be encapsulated in a function argument which could default to replace_args in the non-tagged setting rewritten_arg, tag = cache[b] cache[n] = (replace_args(n, [rewritten_arg]), tags) else: todo.append(b) return cache[s]
def conditional_rewrite(s, rewriting_dict, default_fn=replace_args): """ Apply given rewriter to subexpressions of s bottom up """ todo = [] todo.append(s) cache = {} while todo: n = todo[len(todo) - 1] #print(n) if z3.is_var(n): # No rewriting for variables todo.pop() cache[n] = default_leaf_fn(n) elif z3.is_app(n): # Add non-rewritten children to rewriting stack processed_all_children = True for arg in n.children(): if arg not in cache: todo.append(arg) processed_all_children = False # All children haven been rewritten, so now rewrite n itself if processed_all_children: todo.pop() new_args = [cache[arg] for arg in n.children()] enabled_rewriter = rewriting_dict.get(n.decl()) if enabled_rewriter is not None: #print("Match for {}".format(n.decl())) cache[n] = enabled_rewriter(n, new_args) else: #cache[n] = replace_args(n, new_args) cache[n] = default_fn(n, new_args) else: assert (z3.is_quantifier(n)) b = n.body() if b in cache: # The argument of the quantifier has already been rewritten # Substitute this rewritten term todo.pop() cache[n] = replace_args(n, [cache[b]]) else: todo.append(b) return cache[s]
def rec_go(formula, var_list, reduction_type, q_type, bit_places, polarity): """Recursively go through the formula and apply approximations. """ # Constant if z3.is_const(formula): pass # Variable elif z3.is_var(formula): order = - z3.get_var_index(formula) - 1 # Process if it is bit-vector variable if type(formula) == z3.BitVecRef: # Update max bit-vector width global max_bit_width if max_bit_width < formula.size(): max_bit_width = formula.size() # Approximate if var is quantified in the right way if var_list[order][1] == q_type: formula = approximate(formula, reduction_type, bit_places) # Quantified formula elif type(formula) == z3.QuantifierRef: formula = qform_process(formula, list(var_list), reduction_type, q_type, bit_places, polarity) # Complex formula else: formula = cform_process(formula, list(var_list), reduction_type, q_type, bit_places, polarity) return formula
def unique_leaves(exp, leaf_keys=None): def insert_and_yield(e): k = exp_key(e) if k not in leaf_keys: leaf_keys.append(k) yield e if leaf_keys is None: leaf_keys = [] if z3.is_const(exp) and not (z3.is_int_value(exp) or z3.is_rational_value(exp)): for leaf in insert_and_yield(exp): yield leaf elif z3.is_app(exp): for i in range(exp.num_args()): for leaf in unique_leaves(exp.arg(i), leaf_keys): yield leaf else: assert z3.is_var(exp) for leaf in insert_and_yield(exp): yield leaf
def check_chc_head(expr): if u_predicate(expr): decl = expr.decl() if decl is not None: known_vars = Set([]) for kid in expr.children(): if not z3.is_var(kid): raise Exception("illegal head: " + "argument {} is not a variable {}".format( expr.sexpr(), kid.sexpr())) index = z3.get_var_index(kid) if index in known_vars: raise Exception("illegal head: non-distinct arguments, " + "{} is used twice in {}".format( kid.sexpr(), expr.sexpr())) known_vars.add(z3.get_var_index(kid)) return False elif expr == z3.BoolVal(False): return True else: raise Exception("illegal head: {}".format(expr.sexpr()))
def tree_conditional_rewrite(expr, rewriting_dict, default_fn=replace_args): """Rewrite `expr` bottom up, deliberately ignoring sharing in the DAG. """ if z3.is_var(expr): return default_leaf_fn(expr) elif z3.is_app(expr): new_args = [ tree_conditional_rewrite(child, rewriting_dict, default_fn) for child in expr.children() ] enabled_rewriter = rewriting_dict.get(expr.decl()) if enabled_rewriter is not None: #print("Match for {}".format(n.decl())) return enabled_rewriter(expr, new_args) else: return default_fn(expr, new_args) else: assert (z3.is_quantifier(expr)) new_arg = tree_conditional_rewrite(expr.body(), rewriting_dict, default_fn) return replace_args(expr, new_arg)
def u_predicate(expr, distinct_vars): """Returns True if expr is a valid u_predicate, False if it's not a predicate application, and raises an exception if it's an invalid predicate application. Argument `distinct_vars` forces to check that all arguments are distinct.""" if is_pred_app(expr): vars = set([]) for index, kid in enumerate(expr.children()): if not z3.is_var(kid): raise Exc('Argument {} of application of {} ' 'is not a variable: {}'.format( index, expr.decl().name(), kid.sexpr())) if distinct_vars and kid in vars: raise Exc( 'Illegal predicate application in head: all arguments ' 'need to be distinct variables') vars.add(kid) return True else: return False
def smtify(expr, children): if z3.is_var(expr): # TODO: Allow more than one quantified var? assert str(expr)=='Var(0)', \ 'Currently only support for expressions with a single quantified variable' return '_x_' elif z3.is_quantifier(expr): assert expr.num_vars() == 1, \ 'Currently only support for expressions with a single quantified variable' return '{}({} ((_x_ {}))\n{})'.format( 'forall' if expr.is_forall() else 'exists', expr.var_sort(0), children[0] ) else: #print('{!r} with children {!r}'.format(expr, children)) assert z3.is_app(expr) assert isinstance(encoding, z3.ExprRef) # TODO: Improve/simplify the whitespace handling sjoin = '\n' if multi_line else ' ' child_string = sjoin.join(children) if indent: child_string = textwrap.indent(child_string, indent) stripped = pat.sub(' ', child_string) while stripped[0] == ' ': stripped = stripped[1:] if len(stripped) < 20 or not multi_line: rep = stripped else: rep = '\n' + child_string res = '({} {})'.format( translate_head_func_decl(expr), rep) #print('Will return {}'.format(res)) return res
def check_chc_tail(expr): """Checks that predicate declarations are first, and that there's only one i_formula after them.""" if z3.is_const(expr) \ or z3.is_var(expr) \ or expr.decl().kind() != z3.Z3_OP_AND: raise Exc('Illegal tail: expected conjunction, got {}'.format( expr.sexpr())) else: i_formula_count = 0 for kid in expr.children(): if u_predicate(kid, False): if i_formula_count > 1: raise Exc( "Illegal tail: " "predicate application(s) appears after i_formula") elif i_formula(kid): i_formula_count += 1 else: raise Exc("Illegal conjunct in tail: {}".format(kid.sexpr())) if i_formula_count > 1: raise Exc(("Illegal tail: expected 0 or 1 i_formula, " "got {}").format(i_formula_count)) return True
def unique_var_leaves(exp): for l in unique_leaves(exp): if z3.is_var(l): yield l
def add(expr): if z3.is_var(expr): vars.append(expr)
def var_name(self, rule, i): """ yields the name of a variable """ if z3.is_var(i): i = z3.get_var_index() # (variables are numbered backwards within the predicates) return rule.var_name(rule.num_vars() - 1 - i)
def bound_var_sort (quant, var): if z3.is_var (var): var = z3.get_var_index () return quant.var_sort (quant.num_vars () - 1 - var)
def var_name(self,rule,i): """ yields the name of a variable """ if z3.is_var(i): i = z3.get_var_index() # (variables are numbered backwards within the predicates) return rule.var_name(rule.num_vars()-1-i)
def unique_const_leaves(exp): for l in unique_leaves(exp): if not z3.is_var(l): yield l
def is_pred_app(expr): return (not z3.is_var(expr) and z3.is_expr(expr) and expr.decl().kind() == z3.Z3_OP_UNINTERPRETED)
def i_formula_node(expr): if not z3.is_var(expr) and not z3.is_const(expr): decl = expr.decl() if decl.kind() == z3.Z3_OP_UNINTERPRETED: list.append(False)
def substitute(self, other, expr): def update_term(t, args): n = len(args) # Need to pass an AstArray type into Z3_update_term, not a python list args_ast_arr = (z3.Ast * n)() for i in range(n): args_ast_arr[i] = args[i].as_ast() return _to_expr_ref( z3.Z3_update_term(t.ctx_ref(), t.as_ast(), n, args_ast_arr), t.ctx) cache = z3.AstMap(ctx=expr.ctx) for addr, state in self._expanded_global_state.items(): other_state = other.initial_contract_state(addr) cache[state.storage] = other_state.storage cache[state.balance] = other_state.balance cache[state.nonce] = other_state.nonce fnsubs = [] for k, v in self._cache.items(): if isinstance(v, mem.Memory): # Uses of Memory objects will produce an expression containing # the underlying array object (_mem). If required the index # (_idx) will have been substituted with the actual indexing # expression at that point, so we do not need to consider it # here. cache[v._mem] = getattr(other, k)()._mem elif z3.is_app(v) and v.num_args() > 0: fnsubs.append((v, getattr(other, k)())) else: cache[v] = getattr(other, k)() todo = [expr] while todo: n = todo[-1] if n in cache: todo.pop() elif z3.is_var(n): cache[n] = n todo.pop() elif z3.is_app(n): new_args = [] for i in range(n.num_args()): arg = n.arg(i) if arg not in cache: todo.append(arg) else: new_args.append(cache[arg]) # Only actually do the substitution if all the arguments have # already been processed if len(new_args) == n.num_args(): todo.pop() fn = n.decl() for oldfn, newfn in fnsubs: if z3.eq(fn, oldfn): new_fn = z3.substitute_vars(newfn, *new_args) break else: # TODO only if new_args != old_args if len(new_args) != fn.arity(): new_fn = update_term(n, new_args) else: new_fn = fn(*new_args) cache[n] = new_fn else: assert z3.is_quantifier(n) # Not currently implemented as don't use quanitifers at the # moment raise NotImplementedError() return cache[expr]