Ejemplo n.º 1
0
def generate_wrapper_function(fn: FuncIR, emitter: Emitter) -> None:
    """Generates a CPython-compatible wrapper function for a native function.

    In particular, this handles unboxing the arguments, calling the native function, and
    then boxing the return value.
    """
    emitter.emit_line('{} {{'.format(wrapper_function_header(
        fn, emitter.names)))

    # If fn is a method, then the first argument is a self param
    real_args = list(fn.args)
    if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD:
        arg = real_args.pop(0)
        emitter.emit_line('PyObject *obj_{} = self;'.format(arg.name))

    optional_args = [arg for arg in fn.args if arg.optional]

    arg_names = ''.join('"{}", '.format(arg.name) for arg in real_args)
    emitter.emit_line('static char *kwlist[] = {{{}0}};'.format(arg_names))
    for arg in real_args:
        emitter.emit_line('PyObject *obj_{}{};'.format(
            arg.name, ' = NULL' if arg.optional else ''))
    arg_format = '{}{}:{}'.format(
        'O' * (len(real_args) - len(optional_args)),
        '|' + 'O' * len(optional_args) if len(optional_args) > 0 else '',
        fn.name,
    )
    arg_ptrs = ''.join(', &obj_{}'.format(arg.name) for arg in real_args)
    emitter.emit_lines(
        'if (!PyArg_ParseTupleAndKeywords(args, kw, "{}", kwlist{})) {{'.
        format(arg_format, arg_ptrs), 'return NULL;', '}')
    generate_wrapper_core(fn, emitter, optional_args)
    emitter.emit_line('}')
Ejemplo n.º 2
0
def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
    emitter.emit_lines('typedef struct {',
                       'PyObject_HEAD',
                       'CPyVTableItem *vtable;')
    for attr, rtype in cl.attributes:
        emitter.emit_line('{}{};'.format(rtype.ctype_spaced, attr))
    emitter.emit_line('}} {};'.format(cl.struct_name))
Ejemplo n.º 3
0
def generate_wrapper_function(fn: FuncIR,
                              emitter: Emitter,
                              source_path: str,
                              module_name: str) -> None:
    """Generates a CPython-compatible wrapper function for a native function.

    In particular, this handles unboxing the arguments, calling the native function, and
    then boxing the return value.
    """
    emitter.emit_line('{} {{'.format(wrapper_function_header(fn, emitter.names)))

    # If we hit an error while processing arguments, then we emit a
    # traceback frame to make it possible to debug where it happened.
    # Unlike traceback frames added for exceptions seen in IR, we do this
    # even if there is no `traceback_name`. This is because the error will
    # have originated here and so we need it in the traceback.
    globals_static = emitter.static_name('globals', module_name)
    traceback_code = 'CPy_AddTraceback("%s", "%s", %d, %s);' % (
        source_path.replace("\\", "\\\\"),
        fn.traceback_name or fn.name,
        fn.line,
        globals_static)

    # If fn is a method, then the first argument is a self param
    real_args = list(fn.args)
    if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD:
        arg = real_args.pop(0)
        emitter.emit_line('PyObject *obj_{} = self;'.format(arg.name))

    # Need to order args as: required, optional, kwonly optional, kwonly required
    # This is because CPyArg_ParseTupleAndKeywords format string requires
    # them grouped in that way.
    groups = [[arg for arg in real_args if arg.kind == k] for k in range(ARG_NAMED_OPT + 1)]
    reordered_args = groups[ARG_POS] + groups[ARG_OPT] + groups[ARG_NAMED_OPT] + groups[ARG_NAMED]

    arg_names = ''.join('"{}", '.format(arg.name) for arg in reordered_args)
    emitter.emit_line('static char *kwlist[] = {{{}0}};'.format(arg_names))
    for arg in real_args:
        emitter.emit_line('PyObject *obj_{}{};'.format(
                          arg.name, ' = NULL' if arg.optional else ''))

    cleanups = ['CPy_DECREF(obj_{});'.format(arg.name)
                for arg in groups[ARG_STAR] + groups[ARG_STAR2]]

    arg_ptrs = []  # type: List[str]
    if groups[ARG_STAR] or groups[ARG_STAR2]:
        arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR][0].name) if groups[ARG_STAR] else 'NULL']
        arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR2][0].name) if groups[ARG_STAR2] else 'NULL']
    arg_ptrs += ['&obj_{}'.format(arg.name) for arg in reordered_args]

    emitter.emit_lines(
        'if (!CPyArg_ParseTupleAndKeywords(args, kw, "{}", kwlist{})) {{'.format(
            make_format_string(fn.name, groups), ''.join(', ' + n for n in arg_ptrs)),
        'return NULL;',
        '}')
    generate_wrapper_core(fn, emitter, groups[ARG_OPT] + groups[ARG_NAMED_OPT],
                          cleanups=cleanups,
                          traceback_code=traceback_code)

    emitter.emit_line('}')
Ejemplo n.º 4
0
def generate_getseters(cl: ClassIR, emitter: Emitter) -> None:
    for i, (attr, rtype) in enumerate(cl.attributes.items()):
        generate_getter(cl, attr, rtype, emitter)
        emitter.emit_line('')
        generate_setter(cl, attr, rtype, emitter)
        if i < len(cl.attributes) - 1:
            emitter.emit_line('')
Ejemplo n.º 5
0
 def declare_finals(self, module: str, final_names: Iterable[Tuple[str,
                                                                   RType]],
                    emitter: Emitter) -> None:
     for name, typ in final_names:
         static_name = emitter.static_name(name, module)
         emitter.emit_line('extern {}{};'.format(emitter.ctype_spaced(typ),
                                                 static_name))
Ejemplo n.º 6
0
 def generate_import(self, imp: str, emitter: Emitter, check_for_null: bool) -> None:
     c_name = self.module_static_name(imp, emitter)
     if check_for_null:
         emitter.emit_line('if ({} == NULL) {{'.format(c_name))
     emitter.emit_line('{} = PyImport_ImportModule("{}");'.format(c_name, imp))
     emitter.emit_line('if ({} == NULL)'.format(c_name))
     emitter.emit_line('    return NULL;')
     if check_for_null:
         emitter.emit_line('}')
Ejemplo n.º 7
0
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)))
Ejemplo n.º 8
0
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)))
Ejemplo n.º 9
0
def generate_wrapper_function(fn: FuncIR, emitter: Emitter) -> None:
    """Generates a CPython-compatible wrapper function for a native function.

    In particular, this handles unboxing the arguments, calling the native function, and
    then boxing the return value.
    """
    emitter.emit_line('{} {{'.format(wrapper_function_header(
        fn, emitter.names)))

    # If fn is a method, then the first argument is a self param
    real_args = list(fn.args)
    if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD:
        arg = real_args.pop(0)
        emitter.emit_line('PyObject *obj_{} = self;'.format(arg.name))

    # Need to order args as: required, optional, kwonly optional, kwonly required
    # This is because CPyArg_ParseTupleAndKeywords format string requires
    # them grouped in that way.
    groups = [[arg for arg in real_args if arg.kind == k]
              for k in range(ARG_NAMED_OPT + 1)]
    reordered_args = groups[ARG_POS] + groups[ARG_OPT] + groups[
        ARG_NAMED_OPT] + groups[ARG_NAMED]

    arg_names = ''.join('"{}", '.format(arg.name) for arg in reordered_args)
    emitter.emit_line('static char *kwlist[] = {{{}0}};'.format(arg_names))
    for arg in real_args:
        emitter.emit_line('PyObject *obj_{}{};'.format(
            arg.name, ' = NULL' if arg.optional else ''))

    cleanups = [
        'CPy_DECREF(obj_{});'.format(arg.name)
        for arg in groups[ARG_STAR] + groups[ARG_STAR2]
    ]

    arg_ptrs = []  # type: List[str]
    if groups[ARG_STAR] or groups[ARG_STAR2]:
        arg_ptrs += [
            '&obj_{}'.format(groups[ARG_STAR][0].name)
            if groups[ARG_STAR] else 'NULL'
        ]
        arg_ptrs += [
            '&obj_{}'.format(groups[ARG_STAR2][0].name)
            if groups[ARG_STAR2] else 'NULL'
        ]
    arg_ptrs += ['&obj_{}'.format(arg.name) for arg in reordered_args]

    emitter.emit_lines(
        'if (!CPyArg_ParseTupleAndKeywords(args, kw, "{}", kwlist{})) {{'.
        format(make_format_string(fn.name, groups),
               ''.join(', ' + n for n in arg_ptrs)), 'return NULL;', '}')
    generate_wrapper_core(fn,
                          emitter,
                          groups[ARG_OPT] + groups[ARG_NAMED_OPT],
                          cleanups=cleanups)

    emitter.emit_line('}')
Ejemplo n.º 10
0
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.emit_line('{')
    for attr, rtype in cl.attributes:
        emitter.emit_gc_clear('self->{}'.format(attr), rtype)
    emitter.emit_line('return 0;')
    emitter.emit_line('}')
Ejemplo n.º 11
0
def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter, emitter: Emitter) -> None:
    c_emitter.emit_line('PyTypeObject *{};'.format(emitter.type_struct_name(cl)))
    emitter.emit_line('extern PyTypeObject *{};'.format(emitter.type_struct_name(cl)))
    emitter.emit_line()
    generate_object_struct(cl, emitter)
    emitter.emit_line()
    declare_native_getters_and_setters(cl, emitter)
    generate_full = not cl.is_trait and not cl.builtin_base
    if generate_full:
        emitter.emit_line('{};'.format(native_function_header(cl.ctor, emitter)))
Ejemplo n.º 12
0
def emit_undefined_check(rtype: RType, emitter: Emitter, attr: str, compare: str) -> None:
    if isinstance(rtype, RTuple):
        attr_expr = 'self->{}'.format(attr)
        emitter.emit_line(
            'if ({}) {{'.format(
                emitter.tuple_undefined_check_cond(
                    rtype, attr_expr, emitter.c_undefined_value, compare)))
    else:
        emitter.emit_line(
            'if (self->{} {} {}) {{'.format(attr, compare, emitter.c_undefined_value(rtype)))
Ejemplo n.º 13
0
def generate_new_for_class(cl: ClassIR,
                           func_name: str,
                           vtable_name: str,
                           emitter: Emitter) -> None:
    emitter.emit_line('static PyObject *')
    emitter.emit_line(
        '{}(PyTypeObject *type, PyObject *args, PyObject *kwds)'.format(func_name))
    emitter.emit_line('{')
    # TODO: Check and unbox arguments
    emitter.emit_line('return {}{}();'.format(NATIVE_PREFIX, cl.name))
    emitter.emit_line('}')
Ejemplo n.º 14
0
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('}')
Ejemplo n.º 15
0
 def define_finals(self, final_names: Iterable[Tuple[str, RType]], emitter: Emitter) -> None:
     for name, typ in final_names:
         static_name = emitter.static_name(name, 'final')
         # Here we rely on the fact that undefined value and error value are always the same
         if isinstance(typ, RTuple):
             # We need to inline because initializer must be static
             undefined = '{{ {} }}'.format(''.join(emitter.tuple_undefined_value_helper(typ)))
         else:
             undefined = emitter.c_undefined_value(typ)
         emitter.emit_line('{}{} = {};'.format(emitter.ctype_spaced(typ), static_name,
                                               undefined))
Ejemplo n.º 16
0
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('}')
Ejemplo n.º 17
0
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)))
Ejemplo n.º 18
0
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('}')
Ejemplo n.º 19
0
    def generate_literals(self, emitter: Emitter) -> None:
        emitter.emit_lines('static int CPyLiteralsInit(void)', '{',
                           'static int is_initialized = 0;',
                           'if (is_initialized) return 0;', '')

        for (_, literal), identifier in self.literals.items():
            symbol = emitter.static_name(identifier, None)
            if isinstance(literal, int):
                emitter.emit_line(
                    '{} = PyLong_FromString(\"{}\", NULL, 10);'.format(
                        symbol, str(literal)))
            elif isinstance(literal, float):
                emitter.emit_line('{} = PyFloat_FromDouble({});'.format(
                    symbol, str(literal)))
            elif isinstance(literal, str):
                emitter.emit_line(
                    '{} = PyUnicode_FromStringAndSize({}, {});'.format(
                        symbol, *encode_as_c_string(literal)))
            elif isinstance(literal, bytes):
                emitter.emit_line(
                    '{} = PyBytes_FromStringAndSize({}, {});'.format(
                        symbol, *encode_bytes_as_c_string(literal)))
            else:
                assert False, (
                    'Literals must be integers, floating point numbers, or strings,',
                    'but the provided literal is of type {}'.format(
                        type(literal)))
            emitter.emit_lines('if ({} == NULL)'.format(symbol),
                               '    return -1;')

        emitter.emit_lines(
            'is_initialized = 1;',
            'return 0;',
            '}',
        )
Ejemplo n.º 20
0
    def generate_shared_lib_init(self, emitter: Emitter) -> None:
        """Generate the init function for a shared library.

        A shared library contains all of the actual code for a set of modules.

        The init function is responsible for creating Capsules that wrap
        pointers to the initialization function of all the real init functions
        for modules in this shared library.
        """
        emitter.emit_line()
        emitter.emit_lines(
            'PyMODINIT_FUNC PyInit_{}(void)'.format(self.shared_lib_name),
            '{',
            ('static PyModuleDef def = {{ PyModuleDef_HEAD_INIT, "{}", NULL, -1, NULL, NULL }};'
             .format(self.shared_lib_name)),
            'int res;',
            'PyObject *capsule;',
            'PyObject *module = PyModule_Create(&def);',
            'if (!module) {',
            'goto fail;',
            '}',
            '',
        )

        for mod, _ in self.modules:
            name = exported_name(mod)
            emitter.emit_lines(
                'extern PyObject *CPyInit_{}(void);'.format(name),
                'capsule = PyCapsule_New((void *)CPyInit_{}, "{}.{}", NULL);'.
                format(name, self.shared_lib_name, name),
                'if (!capsule) {',
                'goto fail;',
                '}',
                'res = PyObject_SetAttrString(module, "{}", capsule);'.format(
                    name),
                'Py_DECREF(capsule);',
                'if (res < 0) {',
                'goto fail;',
                '}',
                '',
            )

        emitter.emit_lines(
            'return module;',
            'fail:',
            'Py_XDECREF(module);'
            'return NULL;',
            '}',
        )
Ejemplo n.º 21
0
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
Ejemplo n.º 22
0
def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None:
    for attr, rtype in cl.attributes:
        emitter.emit_line('static PyObject *')
        emitter.emit_line('{}({} *self, void *closure);'.format(getter_name(cl.name, attr),
                                                            cl.struct_name))
        emitter.emit_line('static int')
        emitter.emit_line('{}({} *self, PyObject *value, void *closure);'.format(
            setter_name(cl.name, attr),
            cl.struct_name))
Ejemplo n.º 23
0
def generate_init_for_class(cl: ClassIR,
                            func_name: str,
                            init_fn: FuncIR,
                            emitter: Emitter) -> None:
    """Generate an init function suitable for use as tp_init.

    tp_init needs to be a function that returns an int, and our
    __init__ methods return a PyObject. Translate NULL to -1,
    everything else to 0.
    """
    emitter.emit_line('static int')
    emitter.emit_line(
        '{}(PyObject *self, PyObject *args, PyObject *kwds)'.format(func_name))
    emitter.emit_line('{')
    emitter.emit_line('return {}{}(self, args, kwds) != NULL ? 0 : -1;'.format(
        PREFIX, init_fn.cname(emitter.names)))
    emitter.emit_line('}')
Ejemplo n.º 24
0
def generate_arg_check(name: str, typ: RType, emitter: Emitter) -> None:
    """Insert a runtime check for argument and unbox if necessary.

    The object is named PyObject *obj_{}. This is expected to generate
    a value of name arg_{} (unboxed if necessary). For each primitive a runtime
    check ensures the correct type.
    """
    if typ.is_unboxed:
        # Borrow when unboxing to avoid reference count manipulation.
        emitter.emit_unbox('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                           'return NULL;', declare_dest=True, borrow=True)
    elif is_object_rprimitive(typ):
        # Trivial, since any object is valid.
        emitter.emit_line('PyObject *arg_{} = obj_{};'.format(name, name))
    else:
        emitter.emit_cast('obj_{}'.format(name), 'arg_{}'.format(name), typ,
                          declare_dest=True)
        emitter.emit_line('if (arg_{} == NULL) return NULL;'.format(name))
Ejemplo n.º 25
0
    def generate_from_imports_init_section(self, module_static: str,
                                           imps: List[str],
                                           from_imps: Dict[str,
                                                           List[Tuple[str,
                                                                      str]]],
                                           emitter: Emitter) -> None:
        for imp, import_names in from_imps.items():
            # Only import it again if we haven't imported it from the main
            # imports section
            if imp not in imps:
                c_name = self.module_static_name(imp, emitter)
                emitter.emit_line('CPyModule *{};'.format(c_name))
                self.generate_import(imp, emitter, check_for_null=False)

            for original_name, as_name in import_names:
                # Obtain a reference to the original object
                object_temp_name = emitter.temp_name()
                c_name = self.module_static_name(imp, emitter)
                emitter.emit_line(
                    'PyObject *{} = PyObject_GetAttrString({}, "{}");'.format(
                        object_temp_name,
                        c_name,
                        original_name,
                    ))
                emitter.emit_lines(
                    'if ({} == NULL)'.format(object_temp_name),
                    '    return NULL;',
                )
                # and add it to the namespace of the current module, which eats the ref
                emitter.emit_line(
                    'if (PyModule_AddObject({}, "{}", {}) < 0)'.format(
                        module_static,
                        as_name,
                        object_temp_name,
                    ))
                emitter.emit_line('   return NULL;')

            # This particular import isn't saved as a global so we should decref it
            # and not keep it around
            if imp not in imps:
                c_name = self.module_static_name(imp, emitter)
                emitter.emit_line('Py_DECREF({});'.format(c_name))
Ejemplo n.º 26
0
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('}')
Ejemplo n.º 27
0
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
Ejemplo n.º 28
0
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('}')
Ejemplo n.º 29
0
def generate_native_function(fn: FuncIR, emitter: Emitter, source_path: str,
                             module_name: str) -> None:
    declarations = Emitter(emitter.context, fn.env)
    body = Emitter(emitter.context, fn.env)
    visitor = FunctionEmitterVisitor(body, declarations, fn.name, source_path,
                                     module_name)

    declarations.emit_line('{} {{'.format(native_function_header(fn, emitter)))
    body.indent()

    for r, i in fn.env.indexes.items():
        if isinstance(r.type, RTuple):
            emitter.declare_tuple_struct(r.type)
        if i < len(fn.args):
            continue  # skip the arguments
        ctype = emitter.ctype_spaced(r.type)
        declarations.emit_line('{ctype}{prefix}{name};'.format(
            ctype=ctype, prefix=REG_PREFIX, name=r.name))

    # Before we emit the blocks, give them all labels
    for i, block in enumerate(fn.blocks):
        block.label = i

    for block in fn.blocks:
        body.emit_label(block)
        for op in block.ops:
            op.accept(visitor)

    body.emit_line('}')

    emitter.emit_from_emitter(declarations)
    emitter.emit_from_emitter(body)
Ejemplo n.º 30
0
def generate_wrapper_core(fn: FuncIR,
                          emitter: Emitter,
                          optional_args: List[RuntimeArg] = [],
                          arg_names: Optional[List[str]] = None) -> None:
    """Generates the core part of a wrapper function for a native function.
    This expects each argument as a PyObject * named obj_{arg} as a precondition.
    It converts the PyObject *s to the necessary types, checking and unboxing if necessary,
    makes the call, then boxes the result if necessary and returns it.
    """
    arg_names = arg_names or [arg.name for arg in fn.args]
    for arg_name, arg in zip(arg_names, fn.args):
        generate_arg_check(arg_name, arg.type, emitter, arg in optional_args)
    native_args = ', '.join('arg_{}'.format(arg) for arg in arg_names)
    if fn.ret_type.is_unboxed:
        # TODO: The Py_RETURN macros return the correct PyObject * with reference count handling.
        #       Are they relevant?
        emitter.emit_line('{}retval = {}{}({});'.format(
            emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX,
            fn.cname(emitter.names), native_args))
        emitter.emit_error_check('retval', fn.ret_type, 'return NULL;')
        emitter.emit_box('retval', 'retbox', fn.ret_type, declare_dest=True)
        emitter.emit_line('return retbox;')
    else:
        emitter.emit_line('return {}{}({});'.format(NATIVE_PREFIX,
                                                    fn.cname(emitter.names),
                                                    native_args))