def setup_non_ext_dict(builder: IRBuilder, cdef: ClassDef, metaclass: Value, bases: Value) -> Value: """ Initialize the class dictionary for a non-extension class. This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. has_prepare = builder.primitive_op(py_hasattr_op, [metaclass, builder.load_static_unicode('__prepare__')], cdef.line) non_ext_dict = builder.alloc_temp(dict_rprimitive) true_block, false_block, exit_block, = BasicBlock(), BasicBlock(), BasicBlock() builder.add_bool_branch(has_prepare, true_block, false_block) builder.activate_block(true_block) cls_name = builder.load_static_unicode(cdef.name) prepare_meth = builder.py_get_attr(metaclass, '__prepare__', cdef.line) prepare_dict = builder.py_call(prepare_meth, [cls_name, bases], cdef.line) builder.assign(non_ext_dict, prepare_dict, cdef.line) builder.goto(exit_block) builder.activate_block(false_block) builder.assign(non_ext_dict, builder.primitive_op(new_dict_op, [], cdef.line), cdef.line) builder.goto(exit_block) builder.activate_block(exit_block) return non_ext_dict
def transform_operator_assignment_stmt(builder: IRBuilder, stmt: OperatorAssignmentStmt) -> None: """Operator assignment statement such as x += 1""" builder.disallow_class_assignments([stmt.lvalue], stmt.line) target = builder.get_assignment_target(stmt.lvalue) target_value = builder.read(target, stmt.line) rreg = builder.accept(stmt.rvalue) # the Python parser strips the '=' from operator assignment statements, so re-add it op = stmt.op + '=' res = builder.binary_op(target_value, rreg, op, stmt.line) # usually operator assignments are done in-place # but when target doesn't support that we need to manually assign builder.assign(target, res, res.line)
def translate_next_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: # Special case for calling next() on a generator expression, an # idiom that shows up some in mypy. # # For example, next(x for x in l if x.id == 12, None) will # generate code that searches l for an element where x.id == 12 # and produce the first such object, or None if no such element # exists. if not (expr.arg_kinds in ([ARG_POS], [ARG_POS, ARG_POS]) and isinstance(expr.args[0], GeneratorExpr)): return None gen = expr.args[0] retval = builder.alloc_temp(builder.node_type(expr)) default_val = None if len(expr.args) > 1: default_val = builder.accept(expr.args[1]) exit_block = BasicBlock() def gen_inner_stmts() -> None: # next takes the first element of the generator, so if # something gets produced, we are done. builder.assign(retval, builder.accept(gen.left_expr), gen.left_expr.line) builder.goto(exit_block) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists)) builder.comprehension_helper(loop_params, gen_inner_stmts, gen.line) # Now we need the case for when nothing got hit. If there was # a default value, we produce it, and otherwise we raise # StopIteration. if default_val: builder.assign(retval, default_val, gen.left_expr.line) builder.goto(exit_block) else: builder.add( RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, expr.line)) builder.add(Unreachable()) builder.activate_block(exit_block) return retval
def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: assert len(stmt.lvalues) >= 1 builder.disallow_class_assignments(stmt.lvalues, stmt.line) lvalue = stmt.lvalues[0] if stmt.type and isinstance(stmt.rvalue, TempNode): # This is actually a variable annotation without initializer. Don't generate # an assignment but we need to call get_assignment_target since it adds a # name binding as a side effect. builder.get_assignment_target(lvalue, stmt.line) return line = stmt.rvalue.line rvalue_reg = builder.accept(stmt.rvalue) if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(lvalue, rvalue_reg) for lvalue in stmt.lvalues: target = builder.get_assignment_target(lvalue) builder.assign(target, rvalue_reg, line)
def any_all_helper(builder: IRBuilder, gen: GeneratorExpr, initial_value_op: OpDescription, modify: Callable[[Value], Value], new_value_op: OpDescription) -> Value: retval = builder.alloc_temp(bool_rprimitive) builder.assign(retval, builder.primitive_op(initial_value_op, [], -1), -1) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists)) true_block, false_block, exit_block = BasicBlock(), BasicBlock( ), BasicBlock() def gen_inner_stmts() -> None: comparison = modify(builder.accept(gen.left_expr)) builder.add_bool_branch(comparison, true_block, false_block) builder.activate_block(true_block) builder.assign(retval, builder.primitive_op(new_value_op, [], -1), -1) builder.goto(exit_block) builder.activate_block(false_block) builder.comprehension_helper(loop_params, gen_inner_stmts, gen.line) builder.goto_and_activate(exit_block) return retval
def transform_try_except(builder: IRBuilder, body: GenFunc, handlers: Sequence[ Tuple[Optional[Expression], Optional[Expression], GenFunc]], else_body: Optional[GenFunc], line: int) -> None: """Generalized try/except/else handling that takes functions to gen the bodies. The point of this is to also be able to support with.""" assert handlers, "try needs except" except_entry, exit_block, cleanup_block = BasicBlock(), BasicBlock(), BasicBlock() double_except_block = BasicBlock() # If there is an else block, jump there after the try, otherwise just leave else_block = BasicBlock() if else_body else exit_block # Compile the try block with an error handler builder.builder.push_error_handler(except_entry) builder.goto_and_activate(BasicBlock()) body() builder.goto(else_block) builder.builder.pop_error_handler() # The error handler catches the error and then checks it # against the except clauses. We compile the error handler # itself with an error handler so that it can properly restore # the *old* exc_info if an exception occurs. # The exception chaining will be done automatically when the # exception is raised, based on the exception in exc_info. builder.builder.push_error_handler(double_except_block) builder.activate_block(except_entry) old_exc = builder.maybe_spill(builder.primitive_op(error_catch_op, [], line)) # Compile the except blocks with the nonlocal control flow overridden to clear exc_info builder.nonlocal_control.append( ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc)) # Process the bodies for type, var, handler_body in handlers: next_block = None if type: next_block, body_block = BasicBlock(), BasicBlock() matches = builder.primitive_op( exc_matches_op, [builder.accept(type)], type.line ) builder.add(Branch(matches, body_block, next_block, Branch.BOOL_EXPR)) builder.activate_block(body_block) if var: target = builder.get_assignment_target(var) builder.assign( target, builder.primitive_op(get_exc_value_op, [], var.line), var.line ) handler_body() builder.goto(cleanup_block) if next_block: builder.activate_block(next_block) # Reraise the exception if needed if next_block: builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.nonlocal_control.pop() builder.builder.pop_error_handler() # Cleanup for if we leave except through normal control flow: # restore the saved exc_info information and continue propagating # the exception if it exists. builder.activate_block(cleanup_block) builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line) builder.goto(exit_block) # Cleanup for if we leave except through a raised exception: # restore the saved exc_info information and continue propagating # the exception. builder.activate_block(double_except_block) builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line) builder.primitive_op(keep_propagating_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) # If present, compile the else body in the obvious way if else_body: builder.activate_block(else_block) else_body() builder.goto(exit_block) builder.activate_block(exit_block)