def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: emitter.emit_line('static PyMethodDef {}[] = {{'.format(name)) for fn in cl.methods.values(): if fn.decl.is_prop_setter or fn.decl.is_prop_getter: continue emitter.emit_line('{{"{}",'.format(fn.name)) emitter.emit_line(' (PyCFunction){}{},'.format(PREFIX, fn.cname( emitter.names))) flags = ['METH_VARARGS', 'METH_KEYWORDS'] if fn.decl.kind == FUNC_STATICMETHOD: flags.append('METH_STATIC') elif fn.decl.kind == FUNC_CLASSMETHOD: flags.append('METH_CLASS') emitter.emit_line(' {}, NULL}},'.format(' | '.join(flags))) # Provide a default __getstate__ and __setstate__ if not cl.has_method('__setstate__') and not cl.has_method('__getstate__'): emitter.emit_lines( '{"__setstate__", (PyCFunction)CPyPickle_SetState, METH_O, NULL},', '{"__getstate__", (PyCFunction)CPyPickle_GetState, METH_NOARGS, NULL},', ) emitter.emit_line('{NULL} /* Sentinel */') emitter.emit_line('};')
def generate_richcompare_wrapper(cl: ClassIR, emitter: Emitter) -> Optional[str]: """Generates a wrapper for richcompare dunder methods.""" matches = [name for name in RICHCOMPARE_OPS if cl.has_method(name)] if not matches: return None name = '{}_RichCompare_{}'.format(DUNDER_PREFIX, cl.name_prefix(emitter.names)) emitter.emit_line( 'static PyObject *{name}(PyObject *obj_lhs, PyObject *obj_rhs, int op) {{' .format(name=name)) emitter.emit_line('switch (op) {') for func in matches: emitter.emit_line('case {}: {{'.format(RICHCOMPARE_OPS[func])) method = cl.get_method(func) assert method is not None generate_wrapper_core(method, emitter, arg_names=['lhs', 'rhs']) emitter.emit_line('}') emitter.emit_line('}') emitter.emit_line('Py_INCREF(Py_NotImplemented);') emitter.emit_line('return Py_NotImplemented;') emitter.emit_line('}') return name
def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None: if not cl.is_trait: for attr in cl.attributes: emitter.emit_line('static PyObject *') emitter.emit_line('{}({} *self, void *closure);'.format( getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('static int') emitter.emit_line( '{}({} *self, PyObject *value, void *closure);'.format( setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) for prop in cl.properties: # Generate getter declaration emitter.emit_line('static PyObject *') emitter.emit_line('{}({} *self, void *closure);'.format( getter_name(cl, prop, emitter.names), cl.struct_name(emitter.names))) # Generate property setter declaration if a setter exists if cl.properties[prop][1]: emitter.emit_line('static int') emitter.emit_line( '{}({} *self, PyObject *value, void *closure);'.format( setter_name(cl, prop, emitter.names), cl.struct_name(emitter.names)))
def generate_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None: for attr, rtype in cl.attributes.items(): # Native getter emitter.emit_line('{}{}({} *self)'.format( emitter.ctype_spaced(rtype), native_getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('{') if rtype.is_refcounted: emit_undefined_check(rtype, emitter, attr, '==') emitter.emit_lines( 'PyErr_SetString(PyExc_AttributeError, "attribute {} of {} undefined");' .format(repr(attr), repr(cl.name)), '} else {') emitter.emit_inc_ref('self->{}'.format(attr), rtype) emitter.emit_line('}') emitter.emit_line('return self->{};'.format(attr)) emitter.emit_line('}') emitter.emit_line() # Native setter emitter.emit_line('bool {}({} *self, {}value)'.format( native_setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names), emitter.ctype_spaced(rtype))) emitter.emit_line('{') if rtype.is_refcounted: emit_undefined_check(rtype, emitter, attr, '!=') emitter.emit_dec_ref('self->{}'.format(attr), rtype) emitter.emit_line('}') # This steal the reference to src, so we don't need to increment the arg emitter.emit_lines('self->{} = value;'.format(attr), 'return 1;', '}') emitter.emit_line()
def generate_setup_for_class(cl: ClassIR, func_name: str, defaults_fn: Optional[FuncIR], vtable_name: str, emitter: Emitter) -> None: """Generate a native function that allocates an instance of a class.""" emitter.emit_line('static PyObject *') emitter.emit_line('{}(void)'.format(func_name)) emitter.emit_line('{') emitter.emit_line('{} *self;'.format(cl.struct_name(emitter.names))) emitter.emit_line( 'self = ({struct} *){type_struct}->tp_alloc({type_struct}, 0);'.format( struct=cl.struct_name(emitter.names), type_struct=emitter.type_struct_name(cl))) emitter.emit_line('if (self == NULL)') emitter.emit_line(' return NULL;') emitter.emit_line('self->vtable = {};'.format(vtable_name)) for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_line('self->{} = {};'.format( attr, emitter.c_undefined_value(rtype))) # Initialize attributes to default values, if necessary if defaults_fn is not None: emitter.emit_lines( 'if ({}{}((PyObject *)self) == 0) {{'.format( NATIVE_PREFIX, defaults_fn.cname(emitter.names)), 'Py_DECREF(self);', 'return NULL;', '}') emitter.emit_line('return (PyObject *)self;') emitter.emit_line('}')
def specialize_parent_vtable(cls: ClassIR, parent: ClassIR) -> VTableEntries: """Generate the part of a vtable corresponding to a parent class or trait""" updated = [] for entry in parent.vtable_entries: if isinstance(entry, VTableMethod): # Find the original method corresponding to this vtable entry. # (This may not be the method in the entry, if it was overridden.) orig_parent_method = entry.cls.get_method(entry.name) assert orig_parent_method method_cls = cls.get_method_and_class(entry.name) if method_cls: child_method, defining_cls = method_cls # TODO: emit a wrapper for __init__ that raises or something if (is_same_method_signature(orig_parent_method.sig, child_method.sig) or orig_parent_method.name == '__init__'): entry = VTableMethod(entry.cls, entry.name, child_method, entry.shadow_method) else: entry = VTableMethod( entry.cls, entry.name, defining_cls.glue_methods[(entry.cls, entry.name)], entry.shadow_method) else: # If it is an attribute from a trait, we need to find out # the real class it got mixed in at and point to that. if parent.is_trait: _, origin_cls = cls.attr_details(entry.name) entry = VTableAttr(origin_cls, entry.name, entry.is_setter) updated.append(entry) return updated
def setUp(self) -> None: self.env = Environment() self.n = self.env.add_local(Var('n'), int_rprimitive) self.m = self.env.add_local(Var('m'), int_rprimitive) self.k = self.env.add_local(Var('k'), int_rprimitive) self.l = self.env.add_local(Var('l'), list_rprimitive) # noqa self.ll = self.env.add_local(Var('ll'), list_rprimitive) self.o = self.env.add_local(Var('o'), object_rprimitive) self.o2 = self.env.add_local(Var('o2'), object_rprimitive) self.d = self.env.add_local(Var('d'), dict_rprimitive) self.b = self.env.add_local(Var('b'), bool_rprimitive) self.t = self.env.add_local(Var('t'), RTuple([int_rprimitive, bool_rprimitive])) self.tt = self.env.add_local( Var('tt'), RTuple( [RTuple([int_rprimitive, bool_rprimitive]), bool_rprimitive])) ir = ClassIR('A', 'mod') ir.attributes = OrderedDict([('x', bool_rprimitive), ('y', int_rprimitive)]) compute_vtable(ir) ir.mro = [ir] self.r = self.env.add_local(Var('r'), RInstance(ir)) self.context = EmitterContext(NameGenerator([['mod']])) self.emitter = Emitter(self.context, self.env) self.declarations = Emitter(self.context, self.env) self.visitor = FunctionEmitterVisitor(self.emitter, self.declarations, 'prog.py', 'prog')
def declare_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None: for attr, rtype in cl.attributes.items(): emitter.emit_line('{}{}({} *self);'.format( emitter.ctype_spaced(rtype), native_getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('bool {}({} *self, {}value);'.format( native_setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names), emitter.ctype_spaced(rtype)))
def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None: for attr in cl.attributes: emitter.emit_line('static PyObject *') emitter.emit_line('{}({} *self, void *closure);'.format( getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('static int') emitter.emit_line('{}({} *self, PyObject *value, void *closure);'.format( setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names)))
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: seen_attrs = set() # type: Set[Tuple[str, RType]] lines = [] # type: List[str] lines += ['typedef struct {', 'PyObject_HEAD', 'CPyVTableItem *vtable;'] for base in reversed(cl.base_mro): if not base.is_trait: for attr, rtype in base.attributes.items(): if (attr, rtype) not in seen_attrs: lines.append('{}{};'.format(emitter.ctype_spaced(rtype), emitter.attr(attr))) seen_attrs.add((attr, rtype)) lines.append('}} {};'.format(cl.struct_name(emitter.names))) lines.append('') emitter.context.declarations[cl.struct_name( emitter.names)] = HeaderDeclaration(lines, is_type=True)
def declare_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None: decls = emitter.context.declarations for attr, rtype in cl.attributes.items(): getter_name = native_getter_name(cl, attr, emitter.names) setter_name = native_setter_name(cl, attr, emitter.names) decls[getter_name] = HeaderDeclaration( '{}{}({} *self);'.format(emitter.ctype_spaced(rtype), getter_name, cl.struct_name(emitter.names)), needs_export=True, ) decls[setter_name] = HeaderDeclaration( 'bool {}({} *self, {}value);'.format( native_setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names), emitter.ctype_spaced(rtype)), needs_export=True, )
def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: emitter.emit_line('static int') emitter.emit_line('{}({} *self, PyObject *value, void *closure)'.format( setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('{') if rtype.is_refcounted: emit_undefined_check(rtype, emitter, attr, '!=') emitter.emit_dec_ref('self->{}'.format(attr), rtype) emitter.emit_line('}') emitter.emit_line('if (value != NULL) {') if rtype.is_unboxed: emitter.emit_unbox('value', 'tmp', rtype, custom_failure='return -1;', declare_dest=True) elif is_same_type(rtype, object_rprimitive): emitter.emit_line('PyObject *tmp = value;') else: emitter.emit_cast('value', 'tmp', rtype, declare_dest=True) emitter.emit_lines('if (!tmp)', ' return -1;') emitter.emit_inc_ref('tmp', rtype) emitter.emit_line('self->{} = tmp;'.format(attr)) emitter.emit_line('} else') emitter.emit_line(' self->{} = {};'.format( attr, emitter.c_undefined_value(rtype))) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]: fields = OrderedDict() # type: Dict[str, str] for name, (slot, generator) in table.items(): method = cl.get_method(name) if method: fields[slot] = generator(cl, method, emitter) return fields
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]: fields = OrderedDict() # type: Dict[str, str] # Sort for determinism on Python 3.5 for name, (slot, generator) in sorted(table.items()): method = cl.get_method(name) if method: fields[slot] = generator(cl, method, emitter) return fields
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]: fields = OrderedDict() # type: Dict[str, str] # Sort for determinism on Python 3.5 for name, (slot, generator) in sorted(table.items()): method_cls = cl.get_method_and_class(name) if method_cls and (method_cls[1] == cl or name in ALWAYS_FILL): fields[slot] = generator(cl, method_cls[0], emitter) return fields
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: emitter.emit_lines('typedef struct {', 'PyObject_HEAD', 'CPyVTableItem *vtable;') for base in reversed(cl.base_mro): if not base.is_trait: for attr, rtype in base.attributes.items(): emitter.emit_line('{}{};'.format(emitter.ctype_spaced(rtype), attr)) emitter.emit_line('}} {};'.format(cl.struct_name(emitter.names)))
def generate_side_table_for_class(cl: ClassIR, name: str, type: str, slots: Dict[str, str], emitter: Emitter) -> Optional[str]: name = '{}_{}'.format(cl.name_prefix(emitter.names), name) emitter.emit_line('static {} {} = {{'.format(type, name)) for field, value in slots.items(): emitter.emit_line(".{} = {},".format(field, value)) emitter.emit_line("};") return name
def compute_vtable(cls: ClassIR) -> None: """Compute the vtable structure for a class.""" if cls.vtable is not None: return if not cls.is_generated: cls.has_dict = any(x.inherits_python for x in cls.mro) for t in cls.mro[1:]: # Make sure all ancestors are processed first compute_vtable(t) # Merge attributes from traits into the class if not t.is_trait: continue for name, typ in t.attributes.items(): if not cls.is_trait and not any(name in b.attributes for b in cls.base_mro): cls.attributes[name] = typ cls.vtable = {} if cls.base: assert cls.base.vtable is not None cls.vtable.update(cls.base.vtable) cls.vtable_entries = specialize_parent_vtable(cls, cls.base) # Include the vtable from the parent classes, but handle method overrides. entries = cls.vtable_entries # Traits need to have attributes in the vtable, since the # attributes can be at different places in different classes, but # regular classes can just directly get them. if cls.is_trait: # Traits also need to pull in vtable entries for non-trait # parent classes explicitly. for t in cls.mro: for attr in t.attributes: if attr in cls.vtable: continue cls.vtable[attr] = len(entries) entries.append(VTableAttr(t, attr, is_setter=False)) entries.append(VTableAttr(t, attr, is_setter=True)) all_traits = [t for t in cls.mro if t.is_trait] for t in [cls] + cls.traits: for fn in itertools.chain(t.methods.values()): # TODO: don't generate a new entry when we overload without changing the type if fn == cls.get_method(fn.name): cls.vtable[fn.name] = len(entries) # If the class contains a glue method referring to itself, that is a # shadow glue method to support interpreted subclasses. shadow = cls.glue_methods.get((cls, fn.name)) entries.append(VTableMethod(t, fn.name, fn, shadow)) # Compute vtables for all of the traits that the class implements if not cls.is_trait: for trait in all_traits: compute_vtable(trait) cls.trait_vtables[trait] = specialize_parent_vtable(cls, trait)
def generate_dealloc_for_class(cl: ClassIR, dealloc_func_name: str, clear_func_name: str, emitter: Emitter) -> None: emitter.emit_line('static void') emitter.emit_line('{}({} *self)'.format(dealloc_func_name, cl.struct_name(emitter.names))) emitter.emit_line('{') emitter.emit_line('PyObject_GC_UnTrack(self);') emitter.emit_line('{}(self);'.format(clear_func_name)) emitter.emit_line('Py_TYPE(self)->tp_free((PyObject *)self);') emitter.emit_line('}')
def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: emitter.emit_line('static int') emitter.emit_line('{}({} *self)'.format(func_name, cl.struct_name(emitter.names))) emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_clear('self->{}'.format(attr), rtype) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_setup_for_class(cl: ClassIR, func_name: str, vtable_name: str, emitter: Emitter) -> None: """Generate a native function that allocates an instance of a class.""" emitter.emit_line('static PyObject *') emitter.emit_line('{}(void)'.format(func_name)) emitter.emit_line('{') emitter.emit_line('{} *self;'.format(cl.struct_name(emitter.names))) emitter.emit_line('self = ({struct} *){type_struct}.tp_alloc(&{type_struct}, 0);'.format( struct=cl.struct_name(emitter.names), type_struct=emitter.type_struct_name(cl))) emitter.emit_line('if (self == NULL)') emitter.emit_line(' return NULL;') emitter.emit_line('self->vtable = {};'.format(vtable_name)) for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_line('self->{} = {};'.format(attr, emitter.c_undefined_value(rtype))) emitter.emit_line('return (PyObject *)self;') emitter.emit_line('}')
def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: emitter.emit_line('static int') emitter.emit_line('{}({} *self)'.format(func_name, cl.struct_name(emitter.names))) emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_clear('self->{}'.format(emitter.attr(attr)), rtype) if cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that emitter.emit_gc_clear( '*((PyObject **)((char *)self + sizeof({})))'.format(struct_name), object_rprimitive) emitter.emit_gc_clear( '*((PyObject **)((char *)self + sizeof(PyObject *) + sizeof({})))'. format(struct_name), object_rprimitive) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: """Emit function that performs cycle GC traversal of an instance.""" emitter.emit_line('static int') emitter.emit_line('{}({} *self, visitproc visit, void *arg)'.format( func_name, cl.struct_name(emitter.names))) emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_visit('self->{}'.format(attr), rtype) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: emitter.emit_lines('typedef struct {', 'PyObject_HEAD', 'CPyVTableItem *vtable;') seen_attrs = set() # type: Set[Tuple[str, RType]] for base in reversed(cl.base_mro): if not base.is_trait: for attr, rtype in base.attributes.items(): if (attr, rtype) not in seen_attrs: emitter.emit_line('{}{};'.format( emitter.ctype_spaced(rtype), emitter.attr(attr))) seen_attrs.add((attr, rtype)) emitter.emit_line('}} {};'.format(cl.struct_name(emitter.names)))
def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: """Emit function that performs cycle GC traversal of an instance.""" emitter.emit_line('static int') emitter.emit_line('{}({} *self, visitproc visit, void *arg)'.format( func_name, cl.struct_name(emitter.names))) emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_gc_visit('self->{}'.format(emitter.attr(attr)), rtype) if cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that emitter.emit_gc_visit( '*((PyObject **)((char *)self + sizeof({})))'.format(struct_name), object_rprimitive) emitter.emit_gc_visit( '*((PyObject **)((char *)self + sizeof(PyObject *) + sizeof({})))'. format(struct_name), object_rprimitive) emitter.emit_line('return 0;') emitter.emit_line('}')
def build_type_map(mapper: Mapper, modules: List[MypyFile], graph: Graph, types: Dict[Expression, Type], options: CompilerOptions, errors: Errors) -> None: # Collect all classes defined in everything we are compiling classes = [] for module in modules: module_classes = [ node for node in module.defs if isinstance(node, ClassDef) ] classes.extend([(module, cdef) for cdef in module_classes]) # Collect all class mappings so that we can bind arbitrary class name # references even if there are import cycles. for module, cdef in classes: class_ir = ClassIR(cdef.name, module.fullname, is_trait(cdef), is_abstract=cdef.info.is_abstract) class_ir.is_ext_class = is_extension_class(cdef) # If global optimizations are disabled, turn of tracking of class children if not options.global_opts: class_ir.children = None mapper.type_to_ir[cdef.info] = class_ir # Populate structural information in class IR for extension classes. for module, cdef in classes: with catch_errors(module.path, cdef.line): if mapper.type_to_ir[cdef.info].is_ext_class: prepare_class_def(module.path, module.fullname, cdef, errors, mapper) else: prepare_non_ext_class_def(module.path, module.fullname, cdef, errors, mapper) # Collect all the functions also. We collect from the symbol table # so that we can easily pick out the right copy of a function that # is conditionally defined. for module in modules: for func in get_module_func_defs(module): prepare_func_def(module.fullname, None, func, mapper)
def generate_get_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __get__ methods.""" name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) emitter.emit_line( 'static PyObject *{name}(PyObject *self, PyObject *instance, PyObject *owner) {{' .format(name=name)) emitter.emit_line('instance = instance ? instance : Py_None;') emitter.emit_line('return {}{}(self, instance, owner);'.format( NATIVE_PREFIX, fn.cname(emitter.names))) emitter.emit_line('}') return name
def generate_trait_vtable_setup(cl: ClassIR, vtable_setup_name: str, vtable_name: str, emitter: Emitter) -> None: """Generate a native function that fixes up the trait vtables of a class. This needs to be called before a class is used. """ emitter.emit_line('static bool') emitter.emit_line('{}{}(void)'.format(NATIVE_PREFIX, vtable_setup_name)) emitter.emit_line('{') if cl.trait_vtables and not cl.is_trait: emitter.emit_lines('CPy_FixupTraitVtable({}_vtable, {});'.format( cl.name_prefix(emitter.names), len(cl.trait_vtables))) emitter.emit_line('return 1;') emitter.emit_line('}')
def generate_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None: for attr, rtype in cl.attributes.items(): # Native getter emitter.emit_line('{}{}({} *self)'.format(emitter.ctype_spaced(rtype), native_getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('{') if rtype.is_refcounted: emitter.emit_lines( 'if (self->{} == {}) {{'.format(attr, emitter.c_undefined_value(rtype)), 'PyErr_SetString(PyExc_AttributeError, "attribute {} of {} undefined");'.format( repr(attr), repr(cl.name)), '} else {') emitter.emit_inc_ref('self->{}'.format(attr), rtype) emitter.emit_line('}') emitter.emit_line('return self->{};'.format(attr)) emitter.emit_line('}') emitter.emit_line() # Native setter emitter.emit_line( 'bool {}({} *self, {}value)'.format(native_setter_name(cl, attr, emitter.names), cl.struct_name(emitter.names), emitter.ctype_spaced(rtype))) emitter.emit_line('{') if rtype.is_refcounted: emitter.emit_line('if (self->{} != {}) {{'.format(attr, emitter.c_undefined_value(rtype))) emitter.emit_dec_ref('self->{}'.format(attr), rtype) emitter.emit_line('}') emitter.emit_inc_ref('value'.format(attr), rtype) emitter.emit_lines('self->{} = value;'.format(attr), 'return 1;', '}') emitter.emit_line()
def generate_dunder_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __dunder__ methods to be able to fit into the mapping protocol slot. This specifically means that the arguments are taken as *PyObjects and returned as *PyObjects. """ input_args = ', '.join('PyObject *obj_{}'.format(arg.name) for arg in fn.args) name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) emitter.emit_line('static PyObject *{name}({input_args}) {{'.format( name=name, input_args=input_args, )) generate_wrapper_core(fn, emitter) emitter.emit_line('}') return name