Commit d784e746 authored by Xavier Thompson's avatar Xavier Thompson

Make cypclass wrappers mirror single inheritance of underlying cypclass

parent 7fb4f606
......@@ -102,6 +102,7 @@ class CypclassWrapperInjection(VisitorTransform):
def __call__(self, root):
self.cypclass_wrappers_stack = []
self.nesting_stack = []
self.module_scope = root.scope
return super(CypclassWrapperInjection, self).__call__(root)
def visit_Node(self, node):
......@@ -154,17 +155,45 @@ class CypclassWrapperInjection(VisitorTransform):
if not node_has_suite:
return None
if len(node.base_classes) > 1:
return None
# TODO: take nesting into account for the name
cclass_name = EncodedString("%s_cyp_wrapper" % node.name)
from .ExprNodes import TupleNode
cclass_bases = TupleNode(node.pos, args=[])
bases_args = []
if node.base_classes:
first_base = node.base_classes[0]
if isinstance(first_base, Nodes.CSimpleBaseTypeNode) and first_base.templates is None:
first_base_name = first_base.name
builtin_entry = self.module_scope.lookup(first_base_name)
if builtin_entry is not None:
return
wrapped_first_base = Nodes.CSimpleBaseTypeNode(
first_base.pos,
name = "%s_cyp_wrapper" % first_base_name,
module_path = [],
is_basic_c_type = first_base.is_basic_c_type,
signed = first_base.signed,
complex = first_base.complex,
longness = first_base.longness,
is_self_arg = first_base.is_self_arg,
templates = None
)
bases_args.append(wrapped_first_base)
cclass_bases = TupleNode(node.pos, args=bases_args)
# the underlying cyobject must come first thing after PyObject_HEAD in the memory layout
# long term, only the base class will declare the underlying attribute
stats = []
if not bases_args:
underlying_cyobject = self.synthesize_underlying_cyobject_attribute(node)
stats = [underlying_cyobject]
stats.append(underlying_cyobject)
cclass_body = Nodes.StatListNode(pos=node.pos, stats=stats)
cclass_doc = EncodedString("Python Object wrapper for underlying cypclass %s" % node.name)
wrapper = Nodes.CypclassWrapperDefNode(
......@@ -182,7 +211,7 @@ class CypclassWrapperInjection(VisitorTransform):
in_pxd = node.in_pxd,
doc = cclass_doc,
body = cclass_body,
wrapped_cypclass = node
wrapped_cypclass = node,
)
return wrapper
......@@ -717,9 +746,10 @@ def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry
# initialise PyObject fields
if is_new_return_type and type.wrapper_type:
objstruct_cname = type.wrapper_type.objstruct_cname
cclass_wrapper_base = type.wrapped_base_type.wrapper_type
code.putln("if(self) {")
code.putln("%s * wrapper = new %s();" % (objstruct_cname, objstruct_cname))
code.putln("wrapper->nogil_cyobject = self;")
code.putln("((%s *)wrapper)->nogil_cyobject = self;" % cclass_wrapper_base.objstruct_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)
......
......@@ -5441,7 +5441,15 @@ class CypclassWrapperDefNode(CClassDefNode):
# > access the method of the underlying cyobject from the self argument of the wrapper method
underlying_obj = ExprNodes.AttributeNode(cfunc_method.pos, obj=self_obj, attribute=underlying_name)
cfunc = ExprNodes.AttributeNode(cfunc_method.pos, obj=underlying_obj, attribute=cfunc_name)
empty_declarator = CNameDeclaratorNode(cfunc_method.pos, name="", cname=None)
cast_underlying_obj = ExprNodes.TypecastNode(
cfunc_method.pos,
type = self.wrapped_cypclass.entry.type,
operand = underlying_obj,
typecheck = False
)
cfunc = ExprNodes.AttributeNode(cfunc_method.pos, obj=cast_underlying_obj, attribute=cfunc_name)
# > call to the underlying method
c_call = ExprNodes.SimpleCallNode(
......
......@@ -3961,16 +3961,31 @@ class CypClassType(CppClassType):
# lock_mode string (tri-state: "nolock"/"checklock"/"autolock")
# _mro [CppClassType] or None the Method Resolution Order of this cypclass according to Python
# 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
to_py_function = None
def __init__(self, name, scope, cname, base_classes, templates=None, template_type=None, nogil=0, lock_mode=None, activable=False):
CppClassType.__init__(self, name, scope, cname, base_classes, templates, template_type, nogil)
if base_classes:
self.find_wrapped_base_type(base_classes)
self.lock_mode = lock_mode if lock_mode else "autolock"
self.activable = activable
self._mro = None
self.wrapper_type = None # set during
self.wrapper_type = None
self.wrapped_base_type = None
def find_wrapped_base_type(self, base_classes):
first_wrapped_cypclass_base = None
for base_type in base_classes:
if base_type.is_cyp_class and base_type.wrapper_type:
first_wrapped_cypclass_base = base_type
break
if first_wrapped_cypclass_base:
self.wrapped_base_type = first_wrapped_cypclass_base.wrapped_base_type
else:
self.wrapped_base_type = self
# Return the MRO for this cypclass
# Compute all the mro needed when a previous computation is not available
......@@ -4005,7 +4020,7 @@ class CypClassType(CppClassType):
def create_from_py_utility_code(self, env):
if not self.wrapper_type:
return False
wrapper_objstruct = self.wrapper_type.objstruct_cname
wrapper_objstruct = self.wrapped_base_type.wrapper_type.objstruct_cname
underlying_type_name = self.cname
self.from_py_function = "__Pyx_PyObject_AsCyObject<%s, %s>" % (wrapper_objstruct, underlying_type_name)
return True
......
......@@ -735,6 +735,8 @@ class Scope(object):
entry.already_declared_here()
else:
entry.type.base_classes = base_classes
if cypclass:
entry.type.find_wrapped_base_type(base_classes)
if templates or entry.type.templates:
if templates != entry.type.templates:
error(pos, "Template parameters do not match previous declaration")
......
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