def translate_safe_generator_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: # Special cases for things that consume iterators where we know we # can safely compile a generator into a list. if (len(expr.args) > 0 and expr.arg_kinds[0] == ARG_POS and isinstance(expr.args[0], GeneratorExpr)): if isinstance(callee, MemberExpr): return builder.gen_method_call( builder.accept(callee.expr), callee.name, ([translate_list_comprehension(builder, expr.args[0])] + [builder.accept(arg) for arg in expr.args[1:]]), builder.node_type(expr), expr.line, expr.arg_kinds, expr.arg_names) else: if len(expr.args) == 1 and callee.fullname == "builtins.tuple": val = sequence_from_generator_preallocate_helper( builder, expr.args[0], empty_op_llbuilder=builder.builder.new_tuple_with_length, set_item_op=new_tuple_set_item_op) if val is not None: return val return builder.call_refexpr_with_args( expr, callee, ([translate_list_comprehension(builder, expr.args[0])] + [builder.accept(arg) for arg in expr.args[1:]])) return None
def translate_tuple_from_generator_call( builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: # Special case for simplest tuple creation from a generator, for example # tuple(f(x) for x in other_list/other_tuple) # translate_safe_generator_call() would take care of other cases if this fails. if (len(expr.args) == 1 and expr.arg_kinds[0] == ARG_POS and isinstance(expr.args[0], GeneratorExpr)): return sequence_from_generator_preallocate_helper( builder, expr.args[0], empty_op_llbuilder=builder.builder.new_tuple_with_length, set_item_op=new_tuple_set_item_op) return None
def translate_list_from_generator_call( builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: """Special case for simplest list comprehension. For example: list(f(x) for x in some_list/some_tuple/some_str) 'translate_list_comprehension()' would take care of other cases if this fails. """ if (len(expr.args) == 1 and expr.arg_kinds[0] == ARG_POS and isinstance(expr.args[0], GeneratorExpr)): return sequence_from_generator_preallocate_helper( builder, expr.args[0], empty_op_llbuilder=builder.builder.new_list_op_with_length, set_item_op=new_list_set_item_op) return None
def dict_methods_fast_path(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: # Specialize a common case when list() is called on a dictionary view # method call, for example foo = list(bar.keys()). if not (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]): return None arg = expr.args[0] # Special case for simplest list comprehension, for example # list(x for x in tmp_list) # TODO: The following code should be moved to a new function after # supporting multiple specialize functions if not isinstance(callee, MemberExpr) and isinstance(arg, GeneratorExpr): val = sequence_from_generator_preallocate_helper( builder, arg, empty_op_llbuilder=builder.builder.new_list_op_with_length, set_item_op=new_list_set_item_op) if val is not None: return val if not (isinstance(arg, CallExpr) and not arg.args and isinstance(arg.callee, MemberExpr)): return None base = arg.callee.expr attr = arg.callee.name rtype = builder.node_type(base) if not (is_dict_rprimitive(rtype) and attr in ('keys', 'values', 'items')): return None obj = builder.accept(base) # Note that it is not safe to use fast methods on dict subclasses, so # the corresponding helpers in CPy.h fallback to (inlined) generic logic. if attr == 'keys': return builder.call_c(dict_keys_op, [obj], expr.line) elif attr == 'values': return builder.call_c(dict_values_op, [obj], expr.line) else: return builder.call_c(dict_items_op, [obj], expr.line)