Commit 4516a118 authored by Xavier Thompson's avatar Xavier Thompson

Use a POD struct defining the memory layout of wrappers to access the underlying cyobject

parent 394ab718
...@@ -294,7 +294,7 @@ def NAME(self, ARGDECLS): ...@@ -294,7 +294,7 @@ def NAME(self, ARGDECLS):
stats = [] stats = []
if not cclass_bases.args: if not cclass_bases.args:
# the memory layout for the underlying cyobject should always be the same # the memory layout for the underlying cyobject should always be the same
# -> maybe use a single common base cclass in the future # and match the memory layout of CyPyObject.
underlying_cyobject = self.synthesize_underlying_cyobject_attribute(node) underlying_cyobject = self.synthesize_underlying_cyobject_attribute(node)
stats.append(underlying_cyobject) stats.append(underlying_cyobject)
...@@ -344,7 +344,7 @@ def NAME(self, ARGDECLS): ...@@ -344,7 +344,7 @@ def NAME(self, ARGDECLS):
underlying_name_declarator = Nodes.CNameDeclaratorNode( underlying_name_declarator = Nodes.CNameDeclaratorNode(
node.pos, node.pos,
name=underlying_name, name=underlying_name,
cname=Naming.cypclass_attr_cname cname=Naming.cypclass_wrapper_underlying_attr
) )
underlying_cyobject = Nodes.CVarDefNode( underlying_cyobject = Nodes.CVarDefNode(
...@@ -1023,18 +1023,9 @@ def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry ...@@ -1023,18 +1023,9 @@ def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry
# __new__ can be defined by user and return another type # __new__ can be defined by user and return another type
is_new_return_type = not new_entry or new_entry.type.return_type == type is_new_return_type = not new_entry or new_entry.type.return_type == type
# initialise PyObject fields # allocate and initialise PyObject fields
if is_new_return_type and type.wrapper_type: if is_new_return_type and type.wrapper_type:
objstruct_cname = type.wrapper_type.objstruct_cname generate_cypclass_wrapper_allocation(code, type.wrapper_type)
cclass_wrapper_base = type.wrapped_base_type().wrapper_type
code.putln("if(self) {")
code.putln("%s * wrapper = (%s *) ::operator new(sizeof *wrapper);" % (objstruct_cname, objstruct_cname))
code.putln("((%s *)wrapper)->%s = self;" % (cclass_wrapper_base.objstruct_cname, Naming.cypclass_attr_cname))
code.putln("PyObject * wrapper_as_py = (PyObject *) wrapper;")
code.putln("wrapper_as_py->ob_refcnt = 0;")
code.putln("wrapper_as_py->ob_type = %s;" % type.wrapper_type.typeptr_cname)
code.putln("self->cy_pyobject = wrapper_as_py;")
code.putln("}")
if init_entry: if init_entry:
init_entry = PyrexTypes.best_match(wrapper_arg_types, init_entry = PyrexTypes.best_match(wrapper_arg_types,
...@@ -1124,3 +1115,18 @@ def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry ...@@ -1124,3 +1115,18 @@ def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry
code.putln("return self;") code.putln("return self;")
code.putln("}") code.putln("}")
def generate_cypclass_wrapper_allocation(code, wrapper_type):
"""
Generate allocation and essential setup of the wrapper object.
The cname of the cyobject is assumed to be 'self'.
The cname 'wrapper' is assumed to be available.
"""
objstruct_cname = wrapper_type.objstruct_cname
code.putln("if (self) {")
code.putln("%s * wrapper = (%s *) ::operator new(sizeof *wrapper);" % (objstruct_cname, objstruct_cname))
code.putln("((PyObject *)wrapper)->ob_refcnt = 0;")
code.putln("((PyObject *)wrapper)->ob_type = %s;" % wrapper_type.typeptr_cname)
code.putln("((%s *)wrapper)->%s = self;" % (Naming.cypclass_wrapper_layout_type, Naming.cypclass_wrapper_underlying_attr))
code.putln("self->cy_pyobject = (PyObject *) wrapper;")
code.putln("}")
...@@ -1686,7 +1686,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1686,7 +1686,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# for cyp wrappers, just decrement the atomic counter of the underlying type # for cyp wrappers, just decrement the atomic counter of the underlying type
parent_type = scope.parent_type parent_type = scope.parent_type
if parent_type.is_cyp_wrapper: if parent_type.is_cyp_wrapper:
underlying_attribute_name = Naming.cypclass_attr_cname underlying_attribute_name = Naming.cypclass_wrapper_underlying_attr
self.generate_self_cast(scope, code) self.generate_self_cast(scope, code)
code.putln( code.putln(
"CyObject * p_nogil_cyobject = p->%s;" "CyObject * p_nogil_cyobject = p->%s;"
......
...@@ -166,8 +166,12 @@ exc_vars = (exc_type_name, exc_value_name, exc_tb_name) ...@@ -166,8 +166,12 @@ exc_vars = (exc_type_name, exc_value_name, exc_tb_name)
api_name = pyrex_prefix + "capi__" api_name = pyrex_prefix + "capi__"
# c name for underlying cypclass attribute in cypclass wrappers # cname for the type that defines the essential memory layout of a cypclass wrapper.
cypclass_attr_cname = "nogil_cyobject" cypclass_wrapper_layout_type = "CyPyObject"
# cname for the underlying cypclass attribute in the memory layout of a cypclass wrapper.
cypclass_wrapper_underlying_attr = "nogil_cyobject"
# the h and api guards get changed to: # the h and api guards get changed to:
# __PYX_HAVE__FILENAME (for ascii filenames) # __PYX_HAVE__FILENAME (for ascii filenames)
......
...@@ -4180,7 +4180,6 @@ class CypClassType(CppClassType): ...@@ -4180,7 +4180,6 @@ class CypClassType(CppClassType):
# _mro [CppClassType] or None the Method Resolution Order of this cypclass according to Python # _mro [CppClassType] or None the Method Resolution Order of this cypclass according to Python
# support_wrapper boolean whether this cypclass will be wrapped # support_wrapper boolean whether this cypclass will be wrapped
# wrapper_type PyExtensionType or None the type of the cclass wrapper # wrapper_type PyExtensionType or None the type of the cclass wrapper
# _wrapped_base_type CypClassType or None the type of the oldest wrapped cypclass base
is_cyp_class = 1 is_cyp_class = 1
to_py_function = None to_py_function = None
...@@ -4195,20 +4194,6 @@ class CypClassType(CppClassType): ...@@ -4195,20 +4194,6 @@ class CypClassType(CppClassType):
self.wrapper_type = None self.wrapper_type = None
self._wrapped_base_type = None self._wrapped_base_type = None
# return the oldest left-path superclass such that all intervening classes have a wrapper
def wrapped_base_type(self):
# if the result has already been computed, return it
if self._wrapped_base_type is not None:
return self._wrapped_base_type
# find the first wrapped base (if there is one) and take the same oldest superclass
for base_type in self.base_classes:
if base_type.is_cyp_class and base_type.support_wrapper:
self._wrapped_base_type = base_type.wrapped_base_type()
return self._wrapped_base_type
# if no wrapped base was found, this type is the oldest wrapped base
self._wrapped_base_type = self
return self
# iterate over the direct bases that support wrapping # iterate over the direct bases that support wrapping
def iter_wrapped_base_types(self): def iter_wrapped_base_types(self):
for base_type in self.base_classes: for base_type in self.base_classes:
...@@ -4252,11 +4237,10 @@ class CypClassType(CppClassType): ...@@ -4252,11 +4237,10 @@ class CypClassType(CppClassType):
def create_from_py_utility_code(self, env): def create_from_py_utility_code(self, env):
if not self.wrapper_type: if not self.wrapper_type:
return False return False
wrapper_objstruct = self.wrapped_base_type().wrapper_type.objstruct_cname
underlying_type_name = self.cname underlying_type_name = self.cname
self.from_py_function = "__Pyx_PyObject_AsCyObject<%s, %s>" % (wrapper_objstruct, underlying_type_name) self.from_py_function = "__Pyx_PyObject_AsCyObject<%s>" % underlying_type_name
return True return True
def from_py_call_code(self, source_code, result_code, error_pos, code, def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None): from_py_function=None, error_condition=None):
extra_args = [self.wrapper_type.typeptr_cname if self.wrapper_type else None] extra_args = [self.wrapper_type.typeptr_cname if self.wrapper_type else None]
...@@ -4265,7 +4249,7 @@ class CypClassType(CppClassType): ...@@ -4265,7 +4249,7 @@ class CypClassType(CppClassType):
return self._assign_from_py_code( return self._assign_from_py_code(
source_code, result_code, error_pos, code, from_py_function, error_condition, extra_args=extra_args) source_code, result_code, error_pos, code, from_py_function, error_condition, extra_args=extra_args)
def empty_declaration_code(self): def empty_declaration_code(self):
if self._empty_declaration is None: if self._empty_declaration is None:
self._empty_declaration = self.declaration_code('', deref=1) self._empty_declaration = self.declaration_code('', deref=1)
......
...@@ -75,6 +75,19 @@ ...@@ -75,6 +75,19 @@
int CyObject_TRYWLOCK(); int CyObject_TRYWLOCK();
}; };
/*
* A POD type that has a compatible memory layout with any wrapper for a cypclass.
*
* Serves as a:
* - convenience type to cast a wrapper and access its underlying cyobject pointer.
* - reference for the memory layout that all cypclass wrappers must respect.
*/
struct CyPyObject {
PyObject_HEAD
CyObject * nogil_cyobject;
};
/* All this is made available by member injection inside the module scope */ /* All this is made available by member injection inside the module scope */
struct ActhonResultInterface : public CyObject { struct ActhonResultInterface : public CyObject {
...@@ -200,10 +213,9 @@ ...@@ -200,10 +213,9 @@
* - return NULL * - return NULL
* *
* template: * template:
* - W: the type of the extension type wrapper
* - U: the type of the underlying cypclass * - U: the type of the underlying cypclass
*/ */
template <typename W, typename U> template <typename U>
static inline U* __Pyx_PyObject_AsCyObject(PyObject * ob, PyTypeObject * type) { static inline U* __Pyx_PyObject_AsCyObject(PyObject * ob, PyTypeObject * type) {
// the PyObject is not of the expected type // the PyObject is not of the expected type
if (ob->ob_type != type) { if (ob->ob_type != type) {
...@@ -211,7 +223,7 @@ ...@@ -211,7 +223,7 @@
return NULL; return NULL;
} }
W * wrapper = (W *)ob; CyPyObject * wrapper = (CyPyObject *)ob;
U * underlying = dynamic_cast<U *>(wrapper->nogil_cyobject); U * underlying = dynamic_cast<U *>(wrapper->nogil_cyobject);
// no underlying cyobject: shouldn't happen, playing it safe for now // no underlying cyobject: shouldn't happen, playing it safe for now
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment