Commit 29067ceb authored by Robert Bradshaw's avatar Robert Bradshaw

Avoid generating utility code unless it's actually used.

This should resolve #1757.
parent 074c49b1
...@@ -1623,8 +1623,9 @@ if VALUE is not None: ...@@ -1623,8 +1623,9 @@ if VALUE is not None:
non_py = [ non_py = [
e for e in all_members e for e in all_members
if not e.type.is_pyobject and (not e.type.create_from_py_utility_code(env) if not e.type.is_pyobject and (not e.type.can_coerce_to_pyobject(env)
or not e.type.create_to_py_utility_code(env))] or not e.type.can_coerce_from_pyobject(env))
]
if cinit or non_py: if cinit or non_py:
if cinit: if cinit:
...@@ -1646,6 +1647,10 @@ if VALUE is not None: ...@@ -1646,6 +1647,10 @@ if VALUE is not None:
node.body.stats.append(pickle_func) node.body.stats.append(pickle_func)
else: else:
for e in all_members:
if not e.type.is_pyobject:
e.type.create_to_py_utility_code(env)
e.type.create_from_py_utility_code(env)
all_members_names = sorted([e.name for e in all_members]) all_members_names = sorted([e.name for e in all_members])
checksum = '0x%s' % hashlib.md5(' '.join(all_members_names).encode('utf-8')).hexdigest()[:7] checksum = '0x%s' % hashlib.md5(' '.join(all_members_names).encode('utf-8')).hexdigest()[:7]
unpickle_func_name = '__pyx_unpickle_%s' % node.class_name unpickle_func_name = '__pyx_unpickle_%s' % node.class_name
......
...@@ -34,6 +34,9 @@ class BaseType(object): ...@@ -34,6 +34,9 @@ class BaseType(object):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return False return False
def can_coerce_from_pyobject(self, env):
return False
def can_coerce_to_pystring(self, env, format_spec=None): def can_coerce_to_pystring(self, env, format_spec=None):
return False return False
...@@ -535,6 +538,9 @@ class CTypedefType(BaseType): ...@@ -535,6 +538,9 @@ class CTypedefType(BaseType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return self.typedef_base_type.can_coerce_to_pyobject(env) return self.typedef_base_type.can_coerce_to_pyobject(env)
def can_coerce_from_pyobject(self, env):
return self.typedef_base_type.can_coerce_from_pyobject(env)
class MemoryViewSliceType(PyrexType): class MemoryViewSliceType(PyrexType):
...@@ -835,6 +841,9 @@ class MemoryViewSliceType(PyrexType): ...@@ -835,6 +841,9 @@ class MemoryViewSliceType(PyrexType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
def can_coerce_from_pyobject(self, env):
return True
def check_for_null_code(self, cname): def check_for_null_code(self, cname):
return cname + '.memview' return cname + '.memview'
...@@ -1017,6 +1026,9 @@ class BufferType(BaseType): ...@@ -1017,6 +1026,9 @@ class BufferType(BaseType):
def can_coerce_to_pyobject(self,env): def can_coerce_to_pyobject(self,env):
return True return True
def can_coerce_from_pyobject(self,env):
return True
def as_argument_type(self): def as_argument_type(self):
return self return self
...@@ -1088,6 +1100,9 @@ class PyObjectType(PyrexType): ...@@ -1088,6 +1100,9 @@ class PyObjectType(PyrexType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
def can_coerce_from_pyobject(self, env):
return True
def default_coerced_ctype(self): def default_coerced_ctype(self):
"""The default C type that this Python type coerces to, or None.""" """The default C type that this Python type coerces to, or None."""
return None return None
...@@ -1420,6 +1435,9 @@ class CType(PyrexType): ...@@ -1420,6 +1435,9 @@ class CType(PyrexType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return self.create_to_py_utility_code(env) return self.create_to_py_utility_code(env)
def can_coerce_from_pyobject(self, env):
return self.create_from_py_utility_code(env)
def error_condition(self, result_code): def error_condition(self, result_code):
conds = [] conds = []
if self.is_string or self.is_pyunicode_ptr: if self.is_string or self.is_pyunicode_ptr:
...@@ -1527,6 +1545,9 @@ class CConstType(BaseType): ...@@ -1527,6 +1545,9 @@ class CConstType(BaseType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return self.const_base_type.can_coerce_to_pyobject(env) return self.const_base_type.can_coerce_to_pyobject(env)
def can_coerce_from_pyobject(self, env):
return self.const_base_type.can_coerce_from_pyobject(env)
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
if self.const_base_type.create_to_py_utility_code(env): if self.const_base_type.create_to_py_utility_code(env):
self.to_py_function = self.const_base_type.to_py_function self.to_py_function = self.const_base_type.to_py_function
...@@ -1715,6 +1736,9 @@ class CIntType(CNumericType): ...@@ -1715,6 +1736,9 @@ class CIntType(CNumericType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
def can_coerce_from_pyobject(self, env):
return True
@staticmethod @staticmethod
def _parse_format(format_spec): def _parse_format(format_spec):
padding = ' ' padding = ' '
...@@ -2087,6 +2111,8 @@ class CComplexType(CNumericType): ...@@ -2087,6 +2111,8 @@ class CComplexType(CNumericType):
if (not src_type.is_complex and src_type.is_numeric and src_type.is_typedef if (not src_type.is_complex and src_type.is_numeric and src_type.is_typedef
and src_type.typedef_is_external): and src_type.typedef_is_external):
return False return False
elif src_type.is_pyobject:
return True
else: else:
return super(CComplexType, self).assignable_from(src_type) return super(CComplexType, self).assignable_from(src_type)
...@@ -2139,6 +2165,9 @@ class CComplexType(CNumericType): ...@@ -2139,6 +2165,9 @@ class CComplexType(CNumericType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
def can_coerce_from_pyobject(self, env):
return True
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
env.use_utility_code(UtilityCode.load_cached('ToPy', 'Complex.c')) env.use_utility_code(UtilityCode.load_cached('ToPy', 'Complex.c'))
return True return True
...@@ -2312,6 +2341,9 @@ class CArrayType(CPointerBaseType): ...@@ -2312,6 +2341,9 @@ class CArrayType(CPointerBaseType):
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return self.base_type.can_coerce_to_pyobject(env) return self.base_type.can_coerce_to_pyobject(env)
def can_coerce_from_pyobject(self, env):
return self.base_type.can_coerce_from_pyobject(env)
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
if self.to_py_function is not None: if self.to_py_function is not None:
return self.to_py_function return self.to_py_function
...@@ -3190,20 +3222,20 @@ class CStructOrUnionType(CType): ...@@ -3190,20 +3222,20 @@ class CStructOrUnionType(CType):
self._convert_from_py_code = None self._convert_from_py_code = None
self.packed = packed self.packed = packed
def create_to_py_utility_code(self, env): def can_coerce_to_pyobject(self, env):
if env.outer_scope is None:
return False
if self._convert_to_py_code is False: if self._convert_to_py_code is False:
return None # tri-state-ish return None # tri-state-ish
if env.outer_scope is None:
return False
if self._convert_to_py_code is None: if self._convert_to_py_code is None:
is_union = not self.is_struct is_union = not self.is_struct
unsafe_union_types = set() unsafe_union_types = set()
safe_union_types = set() safe_union_types = set()
for member in self.scope.var_entries: for member in self.scope.var_entries:
member_type = member.type member_type = member.type
if not member_type.create_to_py_utility_code(env): if not member_type.can_coerce_to_pyobject(env):
self.to_py_function = None self.to_py_function = None
self._convert_to_py_code = False self._convert_to_py_code = False
return False return False
...@@ -3219,12 +3251,29 @@ class CStructOrUnionType(CType): ...@@ -3219,12 +3251,29 @@ class CStructOrUnionType(CType):
self._convert_from_py_code = False self._convert_from_py_code = False
return False return False
return True
def create_to_py_utility_code(self, env):
if not self.can_coerce_to_pyobject(env):
return False
if self._convert_to_py_code is None:
for member in self.scope.var_entries:
member.type.create_to_py_utility_code(env)
forward_decl = self.entry.visibility != 'extern' and not self.typedef_flag forward_decl = self.entry.visibility != 'extern' and not self.typedef_flag
self._convert_to_py_code = ToPyStructUtilityCode(self, forward_decl, env) self._convert_to_py_code = ToPyStructUtilityCode(self, forward_decl, env)
env.use_utility_code(self._convert_to_py_code) env.use_utility_code(self._convert_to_py_code)
return True return True
def can_coerce_from_pyobject(self):
if env.outer_scope is None or self._convert_from_py_code is False:
return False
for member in self.scope.var_entries:
if not member.type.assignable_from_resolved_type(PyrexTypes.py_object_type):
return False
return True
def create_from_py_utility_code(self, env): def create_from_py_utility_code(self, env):
if env.outer_scope is None: if env.outer_scope is None:
return False return False
...@@ -3375,6 +3424,9 @@ class CppClassType(CType): ...@@ -3375,6 +3424,9 @@ class CppClassType(CType):
else: else:
return '' return ''
def can_coerce_from_pyobject(self, env):
return self.cname in builtin_cpp_conversions or self.cname in cpp_string_conversions
def create_from_py_utility_code(self, env): def create_from_py_utility_code(self, env):
if self.from_py_function is not None: if self.from_py_function is not None:
return True return True
...@@ -3407,6 +3459,9 @@ class CppClassType(CType): ...@@ -3407,6 +3459,9 @@ class CppClassType(CType):
self.from_py_function = cname self.from_py_function = cname
return True return True
def can_coerce_to_pyobject(self, env):
return self.cname in builtin_cpp_conversions or self.cname in cpp_string_conversions
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
if self.to_py_function is not None: if self.to_py_function is not None:
return True return True
...@@ -3612,7 +3667,8 @@ class CppClassType(CType): ...@@ -3612,7 +3667,8 @@ class CppClassType(CType):
# TODO: handle operator=(...) here? # TODO: handle operator=(...) here?
if other_type is error_type: if other_type is error_type:
return True return True
return other_type.is_cpp_class and other_type.is_subclass(self) elif other_type.is_cpp_class:
return other_type.is_subclass(self)
def attributes_known(self): def attributes_known(self):
return self.scope is not None return self.scope is not None
...@@ -3731,6 +3787,12 @@ class CEnumType(CType): ...@@ -3731,6 +3787,12 @@ class CEnumType(CType):
self.name, self.cname, self.typedef_flag, namespace) self.name, self.cname, self.typedef_flag, namespace)
return self return self
def can_coerce_to_pyobject(self, env):
return True
def can_coerce_from_pyobject(self, env):
return True
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name() self.to_py_function = "__Pyx_PyInt_From_" + self.specialization_name()
env.use_utility_code(TempitaUtilityCode.load_cached( env.use_utility_code(TempitaUtilityCode.load_cached(
...@@ -3797,6 +3859,12 @@ class CTupleType(CType): ...@@ -3797,6 +3859,12 @@ class CTupleType(CType):
return False return False
return True return True
def can_coerce_from_pyobject(self, env):
for component in self.components:
if not component.can_coerce_from_pyobject(env):
return False
return True
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
if self._convert_to_py_code is False: if self._convert_to_py_code is False:
return None # tri-state-ish return None # tri-state-ish
......
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