def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: seen_attrs: Set[Tuple[str, RType]] = set() lines: List[str] = [] lines += ['typedef struct {', 'PyObject_HEAD', 'CPyVTableItem *vtable;'] if cl.has_method('__call__') and emitter.use_vectorcall(): lines.append('vectorcallfunc vectorcall;') 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)) if isinstance(rtype, RTuple): emitter.declare_tuple_struct(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 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_getter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: attr_field = emitter.attr(attr) 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('{') attr_expr = f'self->{attr_field}' # 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 not always_defined: emitter.emit_undefined_attr_check(rtype, attr_expr, '==', unlikely=True) emitter.emit_line('PyErr_SetString(PyExc_AttributeError,') emitter.emit_line(' "attribute {} of {} undefined");'.format(repr(attr), repr(cl.name))) emitter.emit_line('return NULL;') emitter.emit_line('}') emitter.emit_inc_ref(f'self->{attr_field}', rtype) emitter.emit_box(f'self->{attr_field}', 'retval', rtype, declare_dest=True) emitter.emit_line('return retval;') emitter.emit_line('}')
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('{') 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_offset_table(trait_offset_table_name: str, emitter: Emitter, trait: ClassIR, cl: ClassIR) -> None: """Generate attribute offset row of a trait vtable.""" emitter.emit_line( 'size_t {}_scratch[] = {{'.format(trait_offset_table_name)) for attr in trait.attributes: emitter.emit_line('offsetof({}, {}),'.format( cl.struct_name(emitter.names), emitter.attr(attr))) if not trait.attributes: # This is for msvc. emitter.emit_line('0') emitter.emit_line('};') emitter.emit_line('memcpy({name}, {name}_scratch, sizeof({name}));'.format( name=trait_offset_table_name))
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_setup_for_class(cl: ClassIR, func_name: str, defaults_fn: Optional[FuncIR], vtable_name: str, shadow_vtable_name: Optional[str], emitter: Emitter) -> None: """Generate a native function that allocates an instance of a class.""" emitter.emit_line('static PyObject *') emitter.emit_line('{}(PyTypeObject *type)'.format(func_name)) emitter.emit_line('{') emitter.emit_line('{} *self;'.format(cl.struct_name(emitter.names))) emitter.emit_line('self = ({struct} *)type->tp_alloc(type, 0);'.format( struct=cl.struct_name(emitter.names))) emitter.emit_line('if (self == NULL)') emitter.emit_line(' return NULL;') if shadow_vtable_name: emitter.emit_line('if (type != {}) {{'.format(emitter.type_struct_name(cl))) emitter.emit_line('self->vtable = {};'.format(shadow_vtable_name)) emitter.emit_line('} else {') emitter.emit_line('self->vtable = {};'.format(vtable_name)) emitter.emit_line('}') else: emitter.emit_line('self->vtable = {};'.format(vtable_name)) if cl.has_method('__call__') and emitter.use_vectorcall(): name = cl.method_decl('__call__').cname(emitter.names) emitter.emit_line('self->vectorcall = {}{};'.format(PREFIX, name)) for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): emitter.emit_line('self->{} = {};'.format( emitter.attr(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 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_getter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> None: attr_field = emitter.attr(attr) 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('{') attr_expr = 'self->{}'.format(attr_field) emitter.emit_undefined_attr_check(rtype, attr_expr, '==', unlikely=True) emitter.emit_line('PyErr_SetString(PyExc_AttributeError,') emitter.emit_line(' "attribute {} of {} undefined");'.format(repr(attr), repr(cl.name))) emitter.emit_line('return NULL;') emitter.emit_line('}') emitter.emit_inc_ref('self->{}'.format(attr_field), rtype) emitter.emit_box('self->{}'.format(attr_field), 'retval', rtype, declare_dest=True) emitter.emit_line('return retval;') 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(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('}')