Exemple #1
0
 def final_definition(
         self, module: str, name: str, typ: RType, emitter: Emitter) -> str:
     static_name = emitter.static_name(name, module)
     # 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)
     return '{}{} = {};'.format(emitter.ctype_spaced(typ), static_name, undefined)
Exemple #2
0
 def declare_module(self, module_name: str, emitter: Emitter) -> None:
     # We declare two globals for each module:
     # one used internally in the implementation of module init to cache results
     # and prevent infinite recursion in import cycles, and one used
     # by other modules to refer to it.
     internal_static_name = self.module_internal_static_name(module_name, emitter)
     self.declare_global('CPyModule *', internal_static_name, initializer='NULL')
     static_name = emitter.static_name(module_name, None, prefix=MODULE_PREFIX)
     self.declare_global('CPyModule *', static_name)
     self.simple_inits.append((static_name, 'Py_None'))
def generate_traceback_code(fn: FuncIR, emitter: Emitter, source_path: str,
                            module_name: str) -> str:
    # 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)
    return traceback_code
Exemple #4
0
    def generate_globals_init(self, emitter: Emitter) -> None:
        emitter.emit_lines('', 'int CPyGlobalsInit(void)', '{',
                           'static int is_initialized = 0;',
                           'if (is_initialized) return 0;', '')

        emitter.emit_line('CPy_Init();')
        for symbol, fixup in self.simple_inits:
            emitter.emit_line('{} = {};'.format(symbol, fixup))

        for (_, literal), identifier in self.literals.items():
            symbol = emitter.static_name(identifier, None)
            if isinstance(literal, int):
                actual_symbol = symbol
                symbol = INT_PREFIX + symbol
                emitter.emit_line(
                    'PyObject * {} = PyLong_FromString(\"{}\", NULL, 10);'.
                    format(symbol, str(literal)))
            elif isinstance(literal, float):
                emitter.emit_line('{} = PyFloat_FromDouble({});'.format(
                    symbol, str(literal)))
            elif isinstance(literal, complex):
                emitter.emit_line('{} = PyComplex_FromDoubles({}, {});'.format(
                    symbol, str(literal.real), str(literal.imag)))
            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 (unlikely({} == NULL))'.format(symbol),
                               '    return -1;')
            # Ints have an unboxed representation.
            if isinstance(literal, int):
                emitter.emit_line('{} = CPyTagged_FromObject({});'.format(
                    actual_symbol, symbol))
            elif isinstance(literal, str):
                emitter.emit_line(
                    'PyUnicode_InternInPlace(&{});'.format(symbol))

        emitter.emit_lines(
            'is_initialized = 1;',
            'return 0;',
            '}',
        )
Exemple #5
0
    def generate_module_def(self, emitter: Emitter, module_name: str,
                            module: ModuleIR) -> None:
        """Emit the PyModuleDef struct for a module and the module init function."""
        # Emit module methods
        module_prefix = emitter.names.private_name(module_name)
        emitter.emit_line(
            'static PyMethodDef {}module_methods[] = {{'.format(module_prefix))
        for fn in module.functions:
            if fn.class_name is not None or fn.name == TOP_LEVEL_NAME:
                continue
            emitter.emit_line((
                '{{"{name}", (PyCFunction){prefix}{cname}, METH_VARARGS | METH_KEYWORDS, '
                'NULL /* docstring */}},').format(name=fn.name,
                                                  cname=fn.cname(
                                                      emitter.names),
                                                  prefix=PREFIX))
        emitter.emit_line('{NULL, NULL, 0, NULL}')
        emitter.emit_line('};')
        emitter.emit_line()

        # Emit module definition struct
        emitter.emit_lines(
            'static struct PyModuleDef {}module = {{'.format(module_prefix),
            'PyModuleDef_HEAD_INIT,', '"{}",'.format(module_name),
            'NULL, /* docstring */',
            '-1,       /* size of per-interpreter state of the module,',
            '             or -1 if the module keeps state in global variables. */',
            '{}module_methods'.format(module_prefix), '};')
        emitter.emit_line()
        # Emit module init function. If we are compiling just one module, this
        # will be the C API init function. If we are compiling 2+ modules, we
        # generate a shared library for the modules and shims that call into
        # the shared library, and in this case we use an internal module
        # initialized function that will be called by the shim.
        if not self.use_shared_lib:
            declaration = 'PyMODINIT_FUNC PyInit_{}(void)'.format(module_name)
        else:
            declaration = 'PyObject *CPyInit_{}(void)'.format(
                exported_name(module_name))
        emitter.emit_lines(declaration, '{')
        # Store the module reference in a static and return it when necessary.
        # This is separate from the *global* reference to the module that will
        # be populated when it is imported by a compiled module. We want that
        # reference to only be populated when the module has been successfully
        # imported, whereas this we want to have to stop a circular import.
        module_static = self.module_internal_static_name(module_name, emitter)

        emitter.emit_lines('if ({}) {{'.format(module_static),
                           'Py_INCREF({});'.format(module_static),
                           'return {};'.format(module_static), '}')

        emitter.emit_lines(
            '{} = PyModule_Create(&{}module);'.format(module_static,
                                                      module_prefix),
            'if (unlikely({} == NULL))'.format(module_static),
            '    return NULL;')
        emitter.emit_line(
            'PyObject *modname = PyObject_GetAttrString((PyObject *){}, "__name__");'
            .format(module_static))

        module_globals = emitter.static_name('globals', module_name)
        emitter.emit_lines(
            '{} = PyModule_GetDict({});'.format(module_globals, module_static),
            'if (unlikely({} == NULL))'.format(module_globals),
            '    return NULL;')

        # HACK: Manually instantiate generated classes here
        for cl in module.classes:
            if cl.is_generated:
                type_struct = emitter.type_struct_name(cl)
                emitter.emit_lines(
                    '{t} = (PyTypeObject *)CPyType_FromTemplate({t}_template, NULL, modname);'
                    .format(t=type_struct))
                emitter.emit_lines('if (unlikely(!{}))'.format(type_struct),
                                   '    return NULL;')

        emitter.emit_lines('if (CPyGlobalsInit() < 0)', '    return NULL;')

        self.generate_top_level_call(module, emitter)

        emitter.emit_lines('Py_DECREF(modname);')

        emitter.emit_line('return {};'.format(module_static))
        emitter.emit_line('}')
Exemple #6
0
 def declare_static_pyobject(self, identifier: str,
                             emitter: Emitter) -> None:
     symbol = emitter.static_name(identifier, None)
     self.declare_global('PyObject *', symbol)
Exemple #7
0
 def module_internal_static_name(self, module_name: str,
                                 emitter: Emitter) -> str:
     return emitter.static_name(module_name + '_internal',
                                None,
                                prefix=MODULE_PREFIX)
Exemple #8
0
 def declare_internal_globals(self, module_name: str,
                              emitter: Emitter) -> None:
     static_name = emitter.static_name('globals', module_name)
     self.declare_global('PyObject *', static_name)
Exemple #9
0
    def generate_module_def(self, emitter: Emitter, module_name: str,
                            module: ModuleIR) -> None:
        """Emit the PyModuleDef struct for a module and the module init function."""
        # Emit module methods
        module_prefix = emitter.names.private_name(module_name)
        emitter.emit_line(
            f'static PyMethodDef {module_prefix}module_methods[] = {{')
        for fn in module.functions:
            if fn.class_name is not None or fn.name == TOP_LEVEL_NAME:
                continue
            name = short_id_from_name(fn.name, fn.decl.shortname, fn.line)
            if is_fastcall_supported(fn, emitter.capi_version):
                flag = 'METH_FASTCALL'
            else:
                flag = 'METH_VARARGS'
            emitter.emit_line((
                '{{"{name}", (PyCFunction){prefix}{cname}, {flag} | METH_KEYWORDS, '
                'NULL /* docstring */}},').format(name=name,
                                                  cname=fn.cname(
                                                      emitter.names),
                                                  prefix=PREFIX,
                                                  flag=flag))
        emitter.emit_line('{NULL, NULL, 0, NULL}')
        emitter.emit_line('};')
        emitter.emit_line()

        # Emit module definition struct
        emitter.emit_lines(
            f'static struct PyModuleDef {module_prefix}module = {{',
            'PyModuleDef_HEAD_INIT,', f'"{module_name}",',
            'NULL, /* docstring */',
            '-1,       /* size of per-interpreter state of the module,',
            '             or -1 if the module keeps state in global variables. */',
            f'{module_prefix}module_methods', '};')
        emitter.emit_line()
        # Emit module init function. If we are compiling just one module, this
        # will be the C API init function. If we are compiling 2+ modules, we
        # generate a shared library for the modules and shims that call into
        # the shared library, and in this case we use an internal module
        # initialized function that will be called by the shim.
        if not self.use_shared_lib:
            declaration = f'PyMODINIT_FUNC PyInit_{module_name}(void)'
        else:
            declaration = f'PyObject *CPyInit_{exported_name(module_name)}(void)'
        emitter.emit_lines(declaration, '{')
        emitter.emit_line('PyObject* modname = NULL;')
        # Store the module reference in a static and return it when necessary.
        # This is separate from the *global* reference to the module that will
        # be populated when it is imported by a compiled module. We want that
        # reference to only be populated when the module has been successfully
        # imported, whereas this we want to have to stop a circular import.
        module_static = self.module_internal_static_name(module_name, emitter)

        emitter.emit_lines(f'if ({module_static}) {{',
                           f'Py_INCREF({module_static});',
                           f'return {module_static};', '}')

        emitter.emit_lines(
            f'{module_static} = PyModule_Create(&{module_prefix}module);',
            f'if (unlikely({module_static} == NULL))', '    goto fail;')
        emitter.emit_line(
            'modname = PyObject_GetAttrString((PyObject *){}, "__name__");'.
            format(module_static))

        module_globals = emitter.static_name('globals', module_name)
        emitter.emit_lines(
            f'{module_globals} = PyModule_GetDict({module_static});',
            f'if (unlikely({module_globals} == NULL))', '    goto fail;')

        # HACK: Manually instantiate generated classes here
        type_structs: List[str] = []
        for cl in module.classes:
            type_struct = emitter.type_struct_name(cl)
            type_structs.append(type_struct)
            if cl.is_generated:
                emitter.emit_lines(
                    '{t} = (PyTypeObject *)CPyType_FromTemplate('
                    '(PyObject *){t}_template, NULL, modname);'.format(
                        t=type_struct))
                emitter.emit_lines(f'if (unlikely(!{type_struct}))',
                                   '    goto fail;')

        emitter.emit_lines('if (CPyGlobalsInit() < 0)', '    goto fail;')

        self.generate_top_level_call(module, emitter)

        emitter.emit_lines('Py_DECREF(modname);')

        emitter.emit_line(f'return {module_static};')
        emitter.emit_lines('fail:', f'Py_CLEAR({module_static});',
                           'Py_CLEAR(modname);')
        for name, typ in module.final_names:
            static_name = emitter.static_name(name, module_name)
            emitter.emit_dec_ref(static_name, typ, is_xdec=True)
            undef = emitter.c_undefined_value(typ)
            emitter.emit_line(f'{static_name} = {undef};')
        # the type objects returned from CPyType_FromTemplate are all new references
        # so we have to decref them
        for t in type_structs:
            emitter.emit_line(f'Py_CLEAR({t});')
        emitter.emit_line('return NULL;')
        emitter.emit_line('}')