def test_variable_product_dict(self): u1 = self.prog.NewVariable([1, 2], [], self.current_location) u2 = self.prog.NewVariable([3, 4], [], self.current_location) product = cfg_utils.variable_product_dict({"a": u1, "b": u2}) pairs = [{k: a.data for k, a in d.items()} for d in product] self.assertCountEqual(pairs, [ { "a": 1, "b": 3 }, { "a": 1, "b": 4 }, { "a": 2, "b": 3 }, { "a": 2, "b": 4 }, ])
def get_call_combinations(self, node): """Get this function's call records.""" all_combinations = [] signature_data = set() for callargs, ret, node_after_call in self._call_records: try: combinations = cfg_utils.variable_product_dict(callargs) except cfg_utils.TooComplexError: combination = { name: self.ctx.convert.unsolvable.to_binding(node_after_call) for name in callargs } combinations = [combination] ret = self.ctx.new_unsolvable(node_after_call) for combination in combinations: for return_value in ret.bindings: values = list(combination.values()) + [return_value] data = tuple(v.data for v in values) if data in signature_data: # This combination yields a signature we already know is possible continue # Optimization: when only one combination exists, assume it's visible. if (len(combinations) == 1 and len(ret.bindings) == 1 or node_after_call.HasCombination(values)): signature_data.add(data) all_combinations.append( (node_after_call, combination, return_value)) if not all_combinations: # Fallback: Generate signatures only from the definition of the # method, not the way it's being used. param_binding = self.ctx.convert.unsolvable.to_binding(node) params = collections.defaultdict(lambda: param_binding) ret = self.ctx.convert.unsolvable.to_binding(node) all_combinations.append((node, params, ret)) return all_combinations