def instantiate_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> Value: """Create an instance of a callable class for a function. Calls to the function will actually call this instance. Note that fn_info refers to the function being assigned, whereas builder.fn_info refers to the function encapsulating the function being turned into a callable class. """ fitem = fn_info.fitem func_reg = builder.add(Call(fn_info.callable_class.ir.ctor, [], fitem.line)) # Set the environment attribute of the callable class to point at # the environment class defined in the callable class' immediate # outer scope. Note that there are three possible environment # class registers we may use. This depends on what the encapsulating # (parent) function is: # # - A nested function: the callable class is instantiated # from the current callable class' '__call__' function, and hence # the callable class' environment register is used. # - A generator function: the callable class is instantiated # from the '__next__' method of the generator class, and hence the # environment of the generator class is used. # - Regular function: we use the environment of the original function. curr_env_reg = None if builder.fn_info.is_generator: curr_env_reg = builder.fn_info.generator_class.curr_env_reg elif builder.fn_info.is_nested: curr_env_reg = builder.fn_info.callable_class.curr_env_reg elif builder.fn_info.contains_nested: curr_env_reg = builder.fn_info.curr_env_reg if curr_env_reg: builder.add(SetAttr(func_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line)) return func_reg
def call(self, decl: FuncDecl, args: Sequence[Value], arg_kinds: List[int], arg_names: Sequence[Optional[str]], line: int) -> Value: """Call a native function.""" # Normalize args to positionals. args = self.native_args_to_positional(args, arg_kinds, arg_names, decl.sig, line) return self.add(Call(decl, args, line))
def test_call_two_args(self) -> None: decl = FuncDecl('myfn', None, 'mod', FuncSignature([RuntimeArg('m', int_rprimitive), RuntimeArg('n', int_rprimitive)], int_rprimitive)) self.assert_emit(Call(decl, [self.m, self.k], 55), "cpy_r_r0 = CPyDef_myfn(cpy_r_m, cpy_r_k);")
def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'throw' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, 'throw', object_rprimitive, fn_info): typ = builder.add_argument('type', object_rprimitive) val = builder.add_argument('value', object_rprimitive, ARG_OPT) tb = builder.add_argument('traceback', object_rprimitive, ARG_OPT) # Because the value and traceback arguments are optional and hence # can be NULL if not passed in, we have to assign them Py_None if # they are not passed in. none_reg = builder.none_object() builder.assign_if_null(val, lambda: none_reg, builder.fn_info.fitem.line) builder.assign_if_null(tb, lambda: none_reg, builder.fn_info.fitem.line) # Call the helper function using the arguments passed in, and return that result. result = builder.add( Call(fn_decl, [ builder.self(), builder.read(typ), builder.read(val), builder.read(tb), none_reg ], fn_info.fitem.line)) builder.add(Return(result))
def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the '__next__' method for a generator class.""" builder.enter_method(fn_info.generator_class.ir, '__next__', object_rprimitive, fn_info) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [builder.self(), none_reg, none_reg, none_reg, none_reg], fn_info.fitem.line)) builder.add(Return(result)) builder.leave_method()
def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'send' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, 'send', object_rprimitive, fn_info): arg = builder.add_argument('arg', object_rprimitive) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [ builder.self(), none_reg, none_reg, none_reg, builder.read(arg) ], fn_info.fitem.line)) builder.add(Return(result))
def instantiate_env_class(builder: IRBuilder) -> Value: """Assign an environment class to a register named after the given function definition.""" curr_env_reg = builder.add( Call(builder.fn_info.env_class.ctor, [], builder.fn_info.fitem.line)) if builder.fn_info.is_nested: builder.fn_info.callable_class._curr_env_reg = curr_env_reg builder.add( SetAttr(curr_env_reg, ENV_ATTR_NAME, builder.fn_info.callable_class.prev_env_reg, builder.fn_info.fitem.line)) else: builder.fn_info._curr_env_reg = curr_env_reg return curr_env_reg
def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'throw' method for a generator class.""" builder.enter(fn_info) self_reg = builder.read( add_self_to_env(builder.environment, fn_info.generator_class.ir)) # Add the type, value, and traceback variables to the environment. typ = builder.environment.add_local_reg(Var('type'), object_rprimitive, True) val = builder.environment.add_local_reg(Var('value'), object_rprimitive, True) tb = builder.environment.add_local_reg(Var('traceback'), object_rprimitive, True) # Because the value and traceback arguments are optional and hence # can be NULL if not passed in, we have to assign them Py_None if # they are not passed in. none_reg = builder.none_object() builder.assign_if_null(val, lambda: none_reg, builder.fn_info.fitem.line) builder.assign_if_null(tb, lambda: none_reg, builder.fn_info.fitem.line) # Call the helper function using the arguments passed in, and return that result. result = builder.add( Call(fn_decl, [ self_reg, builder.read(typ), builder.read(val), builder.read(tb), none_reg ], fn_info.fitem.line)) builder.add(Return(result)) blocks, env, _, fn_info = builder.leave() # Create the FuncSignature for the throw function. Note that the # value and traceback fields are optional, and are assigned to if # they are not passed in inside the body of the throw function. sig = FuncSignature((RuntimeArg( SELF_NAME, object_rprimitive), RuntimeArg('type', object_rprimitive), RuntimeArg('value', object_rprimitive, ARG_OPT), RuntimeArg('traceback', object_rprimitive, ARG_OPT)), sig.ret_type) throw_fn_decl = FuncDecl('throw', fn_info.generator_class.ir.name, builder.module_name, sig) throw_fn_ir = FuncIR(throw_fn_decl, blocks, env) fn_info.generator_class.ir.methods['throw'] = throw_fn_ir builder.functions.append(throw_fn_ir)
def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: # OK AND NOW THE FUN PART base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs if base_exprs: bases = [builder.accept(x) for x in base_exprs] tp_bases = builder.new_tuple(bases, cdef.line) else: tp_bases = builder.add( LoadErrorValue(object_rprimitive, is_borrowed=True)) modname = builder.load_static_unicode(builder.module_name) template = builder.add( LoadStatic(object_rprimitive, cdef.name + "_template", builder.module_name, NAMESPACE_TYPE)) # Create the class tp = builder.call_c(pytype_from_template_op, [template, tp_bases, modname], cdef.line) # Immediately fix up the trait vtables, before doing anything with the class. ir = builder.mapper.type_to_ir[cdef.info] if not ir.is_trait and not ir.builtin_base: builder.add( Call( FuncDecl(cdef.name + '_trait_vtable_setup', None, builder.module_name, FuncSignature([], bool_rprimitive)), [], -1)) # Populate a '__mypyc_attrs__' field containing the list of attrs builder.call_c(py_setattr_op, [ tp, builder.load_static_unicode('__mypyc_attrs__'), create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info], cdef.line) ], cdef.line) # Save the class builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_static_unicode(cdef.name), tp, ], cdef.line) return tp
def instantiate_generator_class(builder: IRBuilder) -> Value: fitem = builder.fn_info.fitem generator_reg = builder.add(Call(builder.fn_info.generator_class.ir.ctor, [], fitem.line)) # Get the current environment register. If the current function is nested, then the # generator class gets instantiated from the callable class' '__call__' method, and hence # we use the callable class' environment register. Otherwise, we use the original # function's environment register. if builder.fn_info.is_nested: curr_env_reg = builder.fn_info.callable_class.curr_env_reg else: curr_env_reg = builder.fn_info.curr_env_reg # Set the generator class' environment attribute to point at the environment class # defined in the current scope. builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line)) # Set the generator class' environment class' NEXT_LABEL_ATTR_NAME attribute to 0. zero = Integer(0) builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line)) return generator_reg
def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the '__next__' method for a generator class.""" builder.enter(fn_info) self_reg = builder.read( add_self_to_env(builder.environment, fn_info.generator_class.ir)) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [self_reg, none_reg, none_reg, none_reg, none_reg], fn_info.fitem.line)) builder.add(Return(result)) blocks, env, _, fn_info = builder.leave() sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), ), sig.ret_type) next_fn_decl = FuncDecl('__next__', fn_info.generator_class.ir.name, builder.module_name, sig) next_fn_ir = FuncIR(next_fn_decl, blocks, env) fn_info.generator_class.ir.methods['__next__'] = next_fn_ir builder.functions.append(next_fn_ir)