def generate_native_getters_and_setters(cl: ClassIR, emitter: Emitter) -> None: for attr, rtype in cl.attributes.items(): attr_field = emitter.attr(attr) # 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_field, '==') emitter.emit_lines( 'PyErr_SetString(PyExc_AttributeError, "attribute {} of {} undefined");' .format(repr(attr), repr(cl.name)), '} else {') emitter.emit_inc_ref('self->{}'.format(attr_field), rtype) emitter.emit_line('}') emitter.emit_line('return self->{};'.format(attr_field)) 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_field, '!=') emitter.emit_dec_ref('self->{}'.format(attr_field), 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_field), 'return 1;', '}') emitter.emit_line()
def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: attr_field = emitter.attr(attr) 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: attr_expr = 'self->{}'.format(attr_field) emitter.emit_undefined_attr_check(rtype, attr_expr, '!=') emitter.emit_dec_ref('self->{}'.format(attr_field), 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_field)) emitter.emit_line('} else') emitter.emit_line(' self->{} = {};'.format( attr_field, emitter.c_undefined_value(rtype))) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: attr_field = emitter.attr(attr) 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('{') deletable = cl.is_deletable(attr) if not deletable: emitter.emit_line('if (value == NULL) {') emitter.emit_line('PyErr_SetString(PyExc_AttributeError,') emitter.emit_line(' "{} object attribute {} cannot be deleted");'.format(repr(cl.name), repr(attr))) emitter.emit_line('return -1;') emitter.emit_line('}') # HACK: Don't consider refcounted values as always defined, since it's possible to # access uninitialized values via 'gc.get_objects()'. Accessing non-refcounted # values is benign. always_defined = cl.is_always_defined(attr) and not rtype.is_refcounted if rtype.is_refcounted: attr_expr = f'self->{attr_field}' if not always_defined: emitter.emit_undefined_attr_check(rtype, attr_expr, '!=') emitter.emit_dec_ref('self->{}'.format(attr_field), rtype) if not always_defined: emitter.emit_line('}') if deletable: emitter.emit_line('if (value != NULL) {') if rtype.is_unboxed: emitter.emit_unbox('value', 'tmp', rtype, error=ReturnHandler('-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(f'self->{attr_field} = tmp;') if deletable: emitter.emit_line('} else') emitter.emit_line(' self->{} = {};'.format(attr_field, emitter.c_undefined_value(rtype))) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_set_del_item_wrapper_inner(fn: FuncIR, emitter: Emitter, args: Sequence[RuntimeArg]) -> None: for arg in args: generate_arg_check(arg.name, arg.type, emitter, 'goto fail;', False) native_args = ', '.join('arg_{}'.format(arg.name) for arg in args) emitter.emit_line('{}val = {}{}({});'.format( emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names), native_args)) emitter.emit_error_check('val', fn.ret_type, 'goto fail;') emitter.emit_dec_ref('val', fn.ret_type) emitter.emit_line('return 0;') emitter.emit_label('fail') emitter.emit_line('return -1;') emitter.emit_line('}')
def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: attr_field = emitter.attr(attr) 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('{') deletable = cl.is_deletable(attr) if not deletable: emitter.emit_line('if (value == NULL) {') emitter.emit_line('PyErr_SetString(PyExc_AttributeError,') emitter.emit_line( ' "{} object attribute {} cannot be deleted");'.format( repr(cl.name), repr(attr))) emitter.emit_line('return -1;') emitter.emit_line('}') if rtype.is_refcounted: attr_expr = 'self->{}'.format(attr_field) emitter.emit_undefined_attr_check(rtype, attr_expr, '!=') emitter.emit_dec_ref('self->{}'.format(attr_field), rtype) emitter.emit_line('}') if deletable: emitter.emit_line('if (value != NULL) {') if rtype.is_unboxed: emitter.emit_unbox('value', 'tmp', rtype, error=ReturnHandler('-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_field)) if deletable: emitter.emit_line('} else') emitter.emit_line(' self->{} = {};'.format( attr_field, emitter.c_undefined_value(rtype))) emitter.emit_line('return 0;') emitter.emit_line('}')
def generate_contains_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for a native __contains__ method.""" name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) emitter.emit_line( 'static int {name}(PyObject *self, PyObject *obj_item) {{'. format(name=name)) generate_arg_check('item', fn.args[1].type, emitter, ReturnHandler('-1')) emitter.emit_line('{}val = {}{}(self, arg_item);'.format(emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names))) emitter.emit_error_check('val', fn.ret_type, 'return -1;') if is_bool_rprimitive(fn.ret_type): emitter.emit_line('return val;') else: emitter.emit_line('int boolval = PyObject_IsTrue(val);') emitter.emit_dec_ref('val', fn.ret_type) emitter.emit_line('return boolval;') emitter.emit_line('}') return name
def generate_len_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __len__ methods.""" name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) emitter.emit_line( 'static Py_ssize_t {name}(PyObject *self) {{'.format(name=name)) emitter.emit_line('{}retval = {}{}{}(self);'.format( emitter.ctype_spaced(fn.ret_type), emitter.get_group_prefix(fn.decl), NATIVE_PREFIX, fn.cname(emitter.names))) emitter.emit_error_check('retval', fn.ret_type, 'return -1;') if is_int_rprimitive(fn.ret_type): emitter.emit_line('Py_ssize_t val = CPyTagged_AsSsize_t(retval);') else: emitter.emit_line('Py_ssize_t val = PyLong_AsSsize_t(retval);') emitter.emit_dec_ref('retval', fn.ret_type) emitter.emit_line('if (PyErr_Occurred()) return -1;') emitter.emit_line('return val;') emitter.emit_line('}') return name
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('}')