compiler.embedding: cache attribute types (fixes #276).

This commit is contained in:
whitequark 2016-02-25 19:56:45 +00:00
parent d899d7307e
commit f838b8be49
1 changed files with 103 additions and 100 deletions

View File

@ -295,26 +295,9 @@ class StitchingInferencer(Inferencer):
super().__init__(engine) super().__init__(engine)
self.value_map = value_map self.value_map = value_map
self.quote = quote self.quote = quote
self.attr_type_cache = {}
def _unify_attribute(self, result_type, value_node, attr_name, attr_loc, loc): def _compute_value_type(self, object_value, object_type, object_loc, attr_name, loc):
# The inferencer can only observe types, not values; however,
# when we work with host objects, we have to get the values
# somewhere, since host interpreter does not have types.
# Since we have categorized every host object we quoted according to
# its type, we now interrogate every host object we have to ensure
# that we can successfully serialize the value of the attribute we
# are now adding at the code generation stage.
#
# FIXME: We perform exhaustive checks of every known host object every
# time an attribute access is visited, which is potentially quadratic.
# This is done because it is simpler than performing the checks only when:
# * a previously unknown attribute is encountered,
# * a previously unknown host object is encountered;
# which would be the optimal solution.
object_type = value_node.type.find()
for object_value, object_loc in self.value_map[object_type]:
attr_value_type = None
if not hasattr(object_value, attr_name): if not hasattr(object_value, attr_name):
if attr_name.startswith('_'): if attr_name.startswith('_'):
names = set(filter(lambda name: not name.startswith('_'), names = set(filter(lambda name: not name.startswith('_'),
@ -354,10 +337,10 @@ class StitchingInferencer(Inferencer):
# we want f to be defined on the class, not on the instance. # we want f to be defined on the class, not on the instance.
attributes = object_type.constructor.attributes attributes = object_type.constructor.attributes
attr_value = attr_value.__func__ attr_value = attr_value.__func__
is_method = True
else: else:
attributes = object_type.attributes attributes = object_type.attributes
is_method = False
attr_value_type = None
if isinstance(attr_value, list): if isinstance(attr_value, list):
# Fast path for lists of scalars. # Fast path for lists of scalars.
@ -407,6 +390,26 @@ class StitchingInferencer(Inferencer):
IntMonomorphizer(engine=proxy_engine).visit(ast) IntMonomorphizer(engine=proxy_engine).visit(ast)
attr_value_type = ast.type attr_value_type = ast.type
return attributes, attr_value_type
def _unify_attribute(self, result_type, value_node, attr_name, attr_loc, loc):
# The inferencer can only observe types, not values; however,
# when we work with host objects, we have to get the values
# somewhere, since host interpreter does not have types.
# Since we have categorized every host object we quoted according to
# its type, we now interrogate every host object we have to ensure
# that we can successfully serialize the value of the attribute we
# are now adding at the code generation stage.
object_type = value_node.type.find()
for object_value, object_loc in self.value_map[object_type]:
attr_type_key = (id(object_value), attr_name)
try:
attributes, attr_value_type = self.attr_type_cache[attr_type_key]
except KeyError:
attributes, attr_value_type = \
self._compute_value_type(object_value, object_type, object_loc, attr_name, loc)
self.attr_type_cache[attr_type_key] = attributes, attr_value_type
if attr_name not in attributes: if attr_name not in attributes:
# We just figured out what the type should be. Add it. # We just figured out what the type should be. Add it.
attributes[attr_name] = attr_value_type attributes[attr_name] = attr_value_type