Commit 5a76ba3e authored by Stefan Behnel's avatar Stefan Behnel

prevent simple builtin typed exttype attributes from triggering GC support

parent 69561c17
...@@ -859,6 +859,7 @@ class PyObjectType(PyrexType): ...@@ -859,6 +859,7 @@ class PyObjectType(PyrexType):
buffer_defaults = None buffer_defaults = None
is_extern = False is_extern = False
is_subclassed = False is_subclassed = False
is_gc_simple = False
def __str__(self): def __str__(self):
return "Python object" return "Python object"
...@@ -909,6 +910,12 @@ class PyObjectType(PyrexType): ...@@ -909,6 +910,12 @@ class PyObjectType(PyrexType):
return cname return cname
builtin_types_that_cannot_create_refcycles = set([
'bool', 'int', 'long', 'float', 'complex',
'bytearray', 'bytes', 'unicode', 'str', 'basestring'
])
class BuiltinObjectType(PyObjectType): class BuiltinObjectType(PyObjectType):
# objstruct_cname string Name of PyObject struct # objstruct_cname string Name of PyObject struct
...@@ -929,6 +936,7 @@ class BuiltinObjectType(PyObjectType): ...@@ -929,6 +936,7 @@ class BuiltinObjectType(PyObjectType):
self.cname = cname self.cname = cname
self.typeptr_cname = "(&%s)" % cname self.typeptr_cname = "(&%s)" % cname
self.objstruct_cname = objstruct_cname self.objstruct_cname = objstruct_cname
self.is_gc_simple = name in builtin_types_that_cannot_create_refcycles
def set_scope(self, scope): def set_scope(self, scope):
self.scope = scope self.scope = scope
......
...@@ -1769,6 +1769,7 @@ class CClassScope(ClassScope): ...@@ -1769,6 +1769,7 @@ class CClassScope(ClassScope):
# method_table_cname string # method_table_cname string
# getset_table_cname string # getset_table_cname string
# has_pyobject_attrs boolean Any PyObject attributes? # has_pyobject_attrs boolean Any PyObject attributes?
# has_cyclic_pyobject_attrs boolean Any PyObject attributes that may need GC?
# property_entries [Entry] # property_entries [Entry]
# defined boolean Defined in .pxd file # defined boolean Defined in .pxd file
# implemented boolean Defined in .pyx file # implemented boolean Defined in .pyx file
...@@ -1776,24 +1777,30 @@ class CClassScope(ClassScope): ...@@ -1776,24 +1777,30 @@ class CClassScope(ClassScope):
is_c_class_scope = 1 is_c_class_scope = 1
has_pyobject_attrs = False
has_cyclic_pyobject_attrs = False
defined = False
implemented = False
def __init__(self, name, outer_scope, visibility): def __init__(self, name, outer_scope, visibility):
ClassScope.__init__(self, name, outer_scope) ClassScope.__init__(self, name, outer_scope)
if visibility != 'extern': if visibility != 'extern':
self.method_table_cname = outer_scope.mangle(Naming.methtab_prefix, name) self.method_table_cname = outer_scope.mangle(Naming.methtab_prefix, name)
self.getset_table_cname = outer_scope.mangle(Naming.gstab_prefix, name) self.getset_table_cname = outer_scope.mangle(Naming.gstab_prefix, name)
self.has_pyobject_attrs = 0
self.property_entries = [] self.property_entries = []
self.inherited_var_entries = [] self.inherited_var_entries = []
self.defined = 0
self.implemented = 0
def needs_gc(self): def needs_gc(self):
# If the type or any of its base types have Python-valued # If the type or any of its base types have Python-valued
# C attributes, then it needs to participate in GC. # C attributes, then it needs to participate in GC.
return self.has_pyobject_attrs or \ if self.has_cyclic_pyobject_attrs:
(self.parent_type.base_type and return True
self.parent_type.base_type.scope is not None and base_type = self.parent_type.base_type
self.parent_type.base_type.scope.needs_gc()) if base_type and base_type.scope is not None:
return base_type.scope.needs_gc()
elif self.parent_type.is_builtin_type:
return not self.parent_type.is_gc_simple
return False
def declare_var(self, name, type, pos, def declare_var(self, name, type, pos,
cname = None, visibility = 'private', cname = None, visibility = 'private',
...@@ -1819,7 +1826,10 @@ class CClassScope(ClassScope): ...@@ -1819,7 +1826,10 @@ class CClassScope(ClassScope):
entry.is_variable = 1 entry.is_variable = 1
self.var_entries.append(entry) self.var_entries.append(entry)
if type.is_pyobject and name != '__weakref__': if type.is_pyobject and name != '__weakref__':
self.has_pyobject_attrs = 1 self.has_pyobject_attrs = True
if (not type.is_builtin_type
or not type.scope or type.scope.needs_gc()):
self.has_cyclic_pyobject_attrs = True
if visibility not in ('private', 'public', 'readonly'): if visibility not in ('private', 'public', 'readonly'):
error(pos, error(pos,
"Attribute of extension type cannot be declared %s" % visibility) "Attribute of extension type cannot be declared %s" % visibility)
......
...@@ -319,10 +319,10 @@ class GCDependentSlot(InternalMethodSlot): ...@@ -319,10 +319,10 @@ class GCDependentSlot(InternalMethodSlot):
def slot_code(self, scope): def slot_code(self, scope):
if not scope.needs_gc(): if not scope.needs_gc():
return "0" return "0"
if not scope.has_pyobject_attrs: if not scope.has_cyclic_pyobject_attrs:
# if the type does not have object attributes, it can # if the type does not have GC relevant object attributes, it can
# delegate GC methods to its parent - iff the parent # delegate GC methods to its parent - iff the parent functions
# functions are defined in the same module # are defined in the same module
parent_type_scope = scope.parent_type.base_type.scope parent_type_scope = scope.parent_type.base_type.scope
if scope.parent_scope is parent_type_scope.parent_scope: if scope.parent_scope is parent_type_scope.parent_scope:
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name) entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
...@@ -339,10 +339,10 @@ class ConstructorSlot(InternalMethodSlot): ...@@ -339,10 +339,10 @@ class ConstructorSlot(InternalMethodSlot):
self.method = method self.method = method
def slot_code(self, scope): def slot_code(self, scope):
if self.slot_name != 'tp_new' \ if (self.slot_name != 'tp_new'
and scope.parent_type.base_type \ and scope.parent_type.base_type
and not scope.has_pyobject_attrs \ and not scope.has_pyobject_attrs
and not scope.lookup_here(self.method): and not scope.lookup_here(self.method)):
# if the type does not have object attributes, it can # if the type does not have object attributes, it can
# delegate GC methods to its parent - iff the parent # delegate GC methods to its parent - iff the parent
# functions are defined in the same module # functions are defined in the same module
......
# mode: run
# tag: cyclicgc
cimport cython
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
cdef class ExtTypeNoGC:
"""
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
>>> obj = ExtTypeNoGC()
"""
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
cdef class ExtSubTypeNoGC(ExtTypeNoGC):
"""
>>> obj = ExtSubTypeNoGC()
>>> obj = ExtSubTypeNoGC()
>>> obj = ExtSubTypeNoGC()
>>> obj = ExtSubTypeNoGC()
>>> obj = ExtSubTypeNoGC()
>>> obj = ExtSubTypeNoGC()
"""
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
cdef class ExtTypePyArgsNoGC:
"""
>>> obj = ExtTypePyArgsNoGC()
>>> obj = ExtTypePyArgsNoGC()
>>> obj = ExtTypePyArgsNoGC()
>>> obj = ExtTypePyArgsNoGC()
>>> obj = ExtTypePyArgsNoGC()
>>> obj = ExtTypePyArgsNoGC()
"""
cdef bytes b
cdef str s
cdef unicode u
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
cdef class ExtSubTypePyArgsNoGC(ExtTypePyArgsNoGC):
"""
>>> obj = ExtSubTypePyArgsNoGC()
>>> obj = ExtSubTypePyArgsNoGC()
>>> obj = ExtSubTypePyArgsNoGC()
>>> obj = ExtSubTypePyArgsNoGC()
>>> obj = ExtSubTypePyArgsNoGC()
>>> obj = ExtSubTypePyArgsNoGC()
"""
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
cdef class ExtTypePyArgsWithGC:
"""
>>> obj = ExtTypePyArgsWithGC()
>>> obj = ExtTypePyArgsWithGC()
>>> obj = ExtTypePyArgsWithGC()
>>> obj = ExtTypePyArgsWithGC()
>>> obj = ExtTypePyArgsWithGC()
>>> obj = ExtTypePyArgsWithGC()
"""
cdef bytes b
cdef str s
cdef unicode u
cdef list l
@cython.test_fail_if_path_exists('//CClassDefNode[@scope.has_cyclic_pyobject_attrs = True]')
@cython.test_assert_path_exists('//CClassDefNode',
'//CClassDefNode[@scope]',
'//CClassDefNode[@scope.has_cyclic_pyobject_attrs = False]')
cdef class ExtSubTypePyArgsWithGC(ExtTypePyArgsWithGC):
"""
>>> obj = ExtSubTypePyArgsWithGC()
>>> obj = ExtSubTypePyArgsWithGC()
>>> obj = ExtSubTypePyArgsWithGC()
>>> obj = ExtSubTypePyArgsWithGC()
>>> obj = ExtSubTypePyArgsWithGC()
>>> obj = ExtSubTypePyArgsWithGC()
"""
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