def decode(self, data, shared = None): """ Decode a term to its SMT representation. """ if cc.is_symb(data): s = "|{}|".format(cc.get_symb(data)) if s not in self.vars and s not in self.aux_vars: self.aux_vars.append(s) self.commands.append(["declare-const", s, "Term"]) return s elif cc.is_int(data): return ["int", build_int(cc.get_int(data))] elif cc.is_float(data): return ["real", build_real(cc.get_float(data))] elif cc.is_atom(data): return ["atom", build_ilist(cc.get_atom_chars(data))] elif cc.is_list(data): items = cc.get_list_subterms(data) if shared is None: shared = cc.get_shared(data) return ["list", build_tlist([self.decode(item, shared) for item in items])] elif cc.is_tuple(data): items = cc.get_tuple_subterms(data) if shared is None: shared = cc.get_shared(data) return ["tuple", build_tlist([self.decode(item, shared) for item in items])] elif cc.is_bitstring(data): return ["str", build_slist(cc.get_bits(data))] elif cc.is_alias(data): return self.decode(shared[cc.get_alias(data)], shared) clg.debug_info("decoding failed: " + str(data)) assert False
def solve(self): """ Solve a constraint set and returns the result. """ status = self.solver.check_sat() if status == "sat": return cc.mk_sat() elif status == "unsat": self.solver.exit() return cc.mk_unsat() elif status == "unknown": return cc.mk_unknown() elif status == "timeout": return cc.mk_timeout() clg.debug_info("solve: " + str(status)) assert False
def build_spec(self, spec, var): if cc.is_type_any(spec): return "true" elif cc.is_type_float(spec): return ["is-real", var] elif cc.is_type_integer(spec): return ["is-int", var] elif cc.is_type_list(spec): inner_spec = cc.get_inner_type_from_list(spec) name = self.fun_rec_tlist(inner_spec) return [ "and", ["is-list", var], [name, ["lv", var]], ] elif cc.is_type_nonempty_list(spec): inner_spec = cc.get_inner_type_from_nonempty_list(spec) name = self.fun_rec_tlist(inner_spec) return [ "and", ["is-list", var], ["is-tc", ["lv", var]], [name, ["lv", var]], ] elif cc.is_type_tupledet(spec): body = ["and", ["is-tuple", var]] tlist = ["tv", var] for inner_type in cc.get_inner_types_from_tupledet(spec): body.append(["is-tc", tlist]) body.append(self.build_spec(inner_type, ["th", tlist])) tlist = ["tt", tlist] body.append(["is-tn", tlist]) return body elif cc.is_type_tuple(spec): return ["is-tuple", var] elif cc.is_type_union(spec): ret = ["or"] for inner_type in cc.get_inner_types_from_union(spec): ret.append(self.build_spec(inner_type, var)) return ret elif cc.is_type_range(spec): ret = ["and", ["is-int", var]] limits = cc.get_range_bounds_from_range(spec) if cc.has_lower_bound(limits): ret.append( [">=", ["iv", var], build_int(cc.get_lower_bound(limits))]) if cc.has_upper_bound(limits): ret.append( ["<=", ["iv", var], build_int(cc.get_upper_bound(limits))]) return ret elif cc.is_type_atom(spec): return ["is-atom", var] elif cc.is_type_bitstring(spec): segment_size = cc.get_segment_size_from_bitstring(spec) m = int(segment_size.m) n = int(segment_size.n) slist = ["sv", var] axioms = ["and"] axioms.append(["is-str", var]) while m > 0: axioms.append(["is-sc", slist]) slist = ["st", slist] m -= 1 if n == 0: axioms.append(["is-sn", slist]) elif n > 1: axioms.append(SListSpec(slist, build_int(n), self)) return axioms elif cc.is_type_complete_fun(spec): # TODO if a function is to be called with wrong arguments, program must crash par_spec = cc.get_parameters_from_complete_fun(spec) ret_spec = cc.get_rettype_from_fun(spec) name = self.fun_rec_flist(par_spec, ret_spec) return [ "and", ["is-fun", var], ["=", ["fa", ["fv", var]], build_int(len(par_spec))], [name, ["fm", ["fv", var]]], ["not", ["=", ["fm", ["fv", var]], "fn"]] ] elif cc.is_type_generic_fun(spec): par_spec = None ret_spec = cc.get_rettype_from_fun(spec) name = self.fun_rec_flist(par_spec, ret_spec) return [ "and", ["is-fun", var], [name, ["fm", ["fv", var]]], ["not", ["=", ["fm", ["fv", var]], "fn"]] ] elif cc.is_type_atomlit(spec): return ["=", var, self.decode(cc.get_literal_from_atomlit(spec))] elif cc.is_type_integerlit(spec): return [ "=", var, self.decode(cc.get_literal_from_integerlit(spec)) ] elif cc.is_type_userdef(spec): type_name = cc.get_type_name_of_userdef(spec) return ["|{}|".format(type_name), var] clg.debug_info("unknown spec: " + str(spec)) assert False
def encode(self, data, funs=[]): # TODO description if data[0] == "int": return cc.mk_int(parse_int(data[1])) elif data[0] == "real": return cc.mk_float(parse_real(data[1])) elif data[0] == "atom": node = data[1] v = [] while node != "in": v.append(parse_int(node[1])) node = node[2] return cc.mk_atom(v) elif data[0] == "list": node = data[1] v = [] while node != "tn": v.append(self.encode(node[1], funs)) node = node[2] return cc.mk_list(v) elif data[0] == "tuple": node = data[1] v = [] while node != "tn": v.append(self.encode(node[1], funs)) node = node[2] return cc.mk_tuple(v) elif data[0] == "str": node = data[1] v = [] while node != "sn": v.append(node[1] == "true") node = node[2] return cc.mk_bitstring(v) elif data[0] == "fun": # TODO function decoding and encoding assert isinstance(data, list) and len(data) == 2 fv = parse_int(data[1]) # if a cycle (a function calling itself recursively) is found, # it is obvious that the solver has selected an arbitrary term as a value if fv in funs: return cc.mk_any() funs = funs[:] funs.append(fv) # get function info from solver # TODO save function arity and entries to an array val = self.solver.get_value(["fa", data[1]], ["fm", data[1]]) assert isinstance(val, list) and len(val) == 2 assert isinstance(val[0], list) and len(val[0]) == 2 arity = parse_int(expand_lets(val[0][1])) # if arity is less than or greater than 255, we assume it is an arbitrary value selected by the solver # because there is no constraint limiting the function's arity; thus, we set it to zero if arity < 0 or arity > 255: arity = 0 assert isinstance(val[1], list) and len(val[1]) == 2 node = expand_lets(val[1][1]) entries = [] otherwise = None while node != "fn": assert isinstance(node, list) and len(node) == 4 and node[0] == "fc" x = cc.get_list_subterms(self.encode(["list", node[1]], funs)) # keep only entries with argument length equal to arity if len(x) == arity: y = self.encode(node[2], funs) if otherwise is None: otherwise = y else: entries.append(cc.mk_fun_entry(x, y)) node = node[3] if otherwise is None: otherwise = cc.mk_any() return cc.mk_fun(arity, entries, otherwise) clg.debug_info("encoding failed: " + str(data)) assert False
def command_toSolver(self, log_entry, rev): """ Loads the recorded trace to memory. """ opts_normal = { # Internal commands. LogEntry.OP_PARAMS: self.mfa_params, LogEntry.OP_SPEC: self.mfa_spec, LogEntry.OP_UNFOLD_TUPLE: self.unfold_tuple, LogEntry.OP_UNFOLD_LIST: self.unfold_list, LogEntry.OP_MAKE_BITSTR: self.make_bitstr, LogEntry.OP_CONCAT_SEGS: self.concat_segs, LogEntry.OP_FRESH_LAMBDA_WITH_ARITY: self.fresh_closure, LogEntry.OP_EVALUATED_CLOSURE: self.evaluated_closure, # Constraints. LogEntry.OP_GUARD_TRUE: self.guard_true, LogEntry.OP_GUARD_FALSE: self.guard_false, LogEntry.OP_MATCH_EQUAL_TRUE: self.match_equal, LogEntry.OP_MATCH_EQUAL_FALSE: self.match_not_equal, LogEntry.OP_TUPLE_SZ: self.tuple_sz, LogEntry.OP_TUPLE_NOT_SZ: self.tuple_not_sz, LogEntry.OP_TUPLE_NOT_TPL: self.tuple_not_tpl, LogEntry.OP_LIST_NON_EMPTY: self.list_nonempty, LogEntry.OP_LIST_EMPTY: self.list_empty, LogEntry.OP_LIST_NOT_LST: self.list_not_lst, LogEntry.OP_EMPTY_BITSTR: self.empty_bitstr, LogEntry.OP_NONEMPTY_BITSTR: self.nonempty_bitstr, LogEntry.OP_BITMATCH_CONST_TRUE: self.bitmatch_const_true, LogEntry.OP_BITMATCH_CONST_FALSE: self.bitmatch_const_false, LogEntry.OP_BITMATCH_VAR_TRUE: self.bitmatch_var_true, LogEntry.OP_BITMATCH_VAR_FALSE: self.bitmatch_var_false, LogEntry.OP_LAMBDA: self.erl_lambda, # Erlang BIFs or MFAs treated as BIFs. LogEntry.OP_HD: self.head, LogEntry.OP_TL: self.tail, LogEntry.OP_IS_INTEGER: self.is_integer, LogEntry.OP_IS_ATOM: self.is_atom, LogEntry.OP_IS_FLOAT: self.is_float, LogEntry.OP_IS_LIST: self.is_list, LogEntry.OP_IS_TUPLE: self.is_tuple, LogEntry.OP_IS_BOOLEAN: self.is_boolean, LogEntry.OP_IS_NUMBER: self.is_number, LogEntry.OP_PLUS: self.plus, LogEntry.OP_MINUS: self.minus, LogEntry.OP_TIMES: self.times, LogEntry.OP_RDIV: self.rdiv, LogEntry.OP_IDIV_NAT: self.idiv_nat, LogEntry.OP_REM_NAT: self.rem_nat, LogEntry.OP_UNARY: self.unary, LogEntry.OP_EQUAL: self.equal, LogEntry.OP_UNEQUAL: self.unequal, LogEntry.OP_FLOAT: self.to_float, LogEntry.OP_BOGUS: self.bogus, LogEntry.OP_ATOM_NIL: self.atom_nil, LogEntry.OP_ATOM_HEAD: self.atom_head, LogEntry.OP_ATOM_TAIL: self.atom_tail, LogEntry.OP_LIST_TO_TUPLE: self.list_to_tuple, LogEntry.OP_TUPLE_TO_LIST: self.tuple_to_list, LogEntry.OP_LT_INT: self.lt_integers, LogEntry.OP_LT_FLOAT: self.lt_floats, LogEntry.OP_CONS: self.cons, LogEntry.OP_TCONS: self.tcons, LogEntry.OP_POW: self.pow, LogEntry.OP_IS_BITSTRING: self.is_bitstring, LogEntry.OP_IS_FUN: self.is_fun, LogEntry.OP_IS_FUN_WITH_ARITY: self.is_fun_with_arity, LogEntry.OP_TRUNC: self.trunc, LogEntry.OP_BAND: self.band, LogEntry.OP_BXOR: self.bxor, LogEntry.OP_BOR: self.bor } opts_rev = { # Constraints. LogEntry.OP_GUARD_TRUE: self.guard_true_reversed, LogEntry.OP_GUARD_FALSE: self.guard_false_reversed, LogEntry.OP_MATCH_EQUAL_TRUE: self.match_equal_reversed, LogEntry.OP_MATCH_EQUAL_FALSE: self.match_not_equal_reversed, LogEntry.OP_TUPLE_SZ: self.tuple_sz_reversed, LogEntry.OP_TUPLE_NOT_SZ: self.tuple_not_sz_reversed, LogEntry.OP_TUPLE_NOT_TPL: self.tuple_not_tpl_reversed, LogEntry.OP_LIST_NON_EMPTY: self.list_nonempty_reversed, LogEntry.OP_LIST_EMPTY: self.list_empty_reversed, LogEntry.OP_LIST_NOT_LST: self.list_not_lst_reversed, LogEntry.OP_EMPTY_BITSTR: self.empty_bitstr_reversed, LogEntry.OP_NONEMPTY_BITSTR: self.nonempty_bitstr_reversed, LogEntry.OP_BITMATCH_CONST_TRUE: self.bitmatch_const_true_reversed, LogEntry.OP_BITMATCH_CONST_FALSE: self.bitmatch_const_false_reversed, LogEntry.OP_BITMATCH_VAR_TRUE: self.bitmatch_var_true_reversed, LogEntry.OP_BITMATCH_VAR_FALSE: self.bitmatch_var_false_reversed, LogEntry.OP_NOT_LAMBDA_WITH_ARITY: self.not_lambda_with_arity_reversed, LogEntry.OP_LAMBDA: self.erl_lambda_reversed, # Erlang BIFs or MFAs treated as BIFs. LogEntry.OP_HD: self.head_reversed, LogEntry.OP_TL: self.tail_reversed } # Call the appropriate function with the given payload. opts = opts_rev if rev else opts_normal if log_entry.type not in opts: fmt = ("Unknown JSON Command with\n" " Data: {}\n" " Rev: {}") clg.debug_info(fmt.format(str(log_entry), rev)) args = [log_entry.spec] if log_entry.type == LogEntry.OP_SPEC else log_entry.arguments opts[log_entry.type](*args)