Commit ec37469d authored by Stefan Behnel's avatar Stefan Behnel

reuse object's tp_new() for simple extension types if possible to improve...

reuse object's tp_new() for simple extension types if possible to improve support for multiple inheritance
parent 85f5c24a
......@@ -1068,7 +1068,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type.declaration_code("")))
def generate_new_function(self, scope, code, cclass_entry):
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
# keep this function in sync with ConstructorSlot.can_reuse_baseobject_tp_new()
tp_slot = TypeSlots.ConstructorSlot("tp_new")
slot_func = scope.mangle_internal("tp_new")
type = scope.parent_type
base_type = type.base_type
......@@ -1188,7 +1189,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
"}")
def generate_dealloc_function(self, scope, code):
tp_slot = TypeSlots.ConstructorSlot("tp_dealloc", '__dealloc__')
tp_slot = TypeSlots.DestructorSlot("tp_dealloc", '__dealloc__')
slot_func = scope.mangle_internal("tp_dealloc")
base_type = scope.parent_type.base_type
if tp_slot.slot_code(scope) != slot_func:
......
......@@ -1725,7 +1725,7 @@ class FuncDefNode(StatNode, BlockNode):
self.getbuffer_init(code)
# ----- Create closure scope object
if self.needs_closure:
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
tp_slot = TypeSlots.ConstructorSlot("tp_new")
slot_func_cname = TypeSlots.get_slot_function(lenv.scope_class.type.scope, tp_slot)
if not slot_func_cname:
slot_func_cname = '%s->tp_new' % lenv.scope_class.type.typeptr_cname
......
......@@ -2312,7 +2312,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
if (ext_type.is_extension_type and ext_type.typeobj_cname and
ext_type.scope.global_scope() == self.current_env().global_scope()):
# known type in current module
tp_slot = TypeSlots.ConstructorSlot("tp_new", '__new__')
tp_slot = TypeSlots.ConstructorSlot("tp_new")
slot_func_cname = TypeSlots.get_slot_function(ext_type.scope, tp_slot)
if slot_func_cname:
cython_scope = self.context.cython_scope
......
......@@ -343,15 +343,44 @@ class GCClearReferencesSlot(GCDependentSlot):
class ConstructorSlot(InternalMethodSlot):
# Descriptor for tp_new and tp_dealloc.
# Descriptor for tp_new.
def __init__(self, slot_name):
assert slot_name == 'tp_new'
InternalMethodSlot.__init__(self, slot_name)
def can_reuse_baseobject_tp_new(self, scope):
# keep in sync with generate_new_function() in ModuleNode.py
type = scope.parent_type
if type.base_type or type.vtabslot_cname:
return False
if scope.lookup_here("__new__") or scope.directives.get('freelist', 0):
return False
for entry in scope.var_entries:
if not entry.type.is_pyobject or entry.name != '__weakref__':
return False
return True
def generate_dynamic_init_code(self, scope, code):
if not self.can_reuse_baseobject_tp_new(scope):
return
# Looks like we don't need our own tp_new(), and our object size didn't
# change from our base class PyObject. If we inherit tp_new() from
# plain object, CPython will be less picky on multiple inheritance.
code.putln("%s.tp_new = PyBaseObject_Type.tp_new;" %
scope.parent_type.typeobj_cname)
class DestructorSlot(InternalMethodSlot):
# Descriptor for tp_dealloc.
def __init__(self, slot_name, method, **kargs):
assert slot_name == 'tp_dealloc'
InternalMethodSlot.__init__(self, slot_name, **kargs)
self.method = method
def slot_code(self, scope):
if (self.slot_name != 'tp_new'
and scope.parent_type.base_type
if (scope.parent_type.base_type
and not scope.has_pyobject_attrs
and not scope.lookup_here(self.method)):
# if the type does not have object attributes, it can
......@@ -743,7 +772,7 @@ PyBufferProcs = (
#------------------------------------------------------------------------------------------
slot_table = (
ConstructorSlot("tp_dealloc", '__dealloc__'),
DestructorSlot("tp_dealloc", '__dealloc__'),
EmptySlot("tp_print"), #MethodSlot(printfunc, "tp_print", "__print__"),
EmptySlot("tp_getattr"),
EmptySlot("tp_setattr"),
......@@ -791,7 +820,7 @@ slot_table = (
MethodSlot(initproc, "tp_init", "__init__"),
EmptySlot("tp_alloc"), #FixedSlot("tp_alloc", "PyType_GenericAlloc"),
InternalMethodSlot("tp_new"),
ConstructorSlot("tp_new"),
EmptySlot("tp_free"),
EmptySlot("tp_is_gc"),
......
# mode: run
"""
http://thread.gmane.org/gmane.comp.python.cython.user/10267
The problem here was that CPython complained about calls to a type's
__new__() being unsafe when it has a different tp_new() than object
but the same memory layout.
"""
cdef class A(object):
# larger than object
cdef int a
cdef class B(object):
# same memory layout as object
pass
cinit_called = []
cdef class Cinit(object):
# same memory layout as object but with __cinit__
def __cinit__(self):
cinit_called.append(self)
class C(object):
"""
>>> o = C.__new__(C)
"""
# subtypes of B
class CB(C,B):
"""
>>> o = CB.__new__(CB)
"""
class BC(B,C):
"""
>>> o = BC.__new__(BC)
"""
# subtypes of A
class CA(C,A):
"""
>>> o = CA.__new__(CA)
"""
class AC(A,C):
"""
>>> o = AC.__new__(AC)
"""
# subtypes of Cinit
class CCinit(C, Cinit):
"""
>>> o = CCinit.__new__(CCinit) # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: ...is not safe...
>>> del cinit_called[:]
"""
class CinitC(Cinit, C):
"""
>>> o = CinitC.__new__(CinitC)
>>> o in cinit_called
True
>>> del cinit_called[:]
"""
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