1.1 --- a/deducer.py Sat Oct 15 23:32:15 2016 +0200
1.2 +++ b/deducer.py Sat Oct 15 23:34:29 2016 +0200
1.3 @@ -399,7 +399,8 @@
1.4 locations.sort()
1.5
1.6 for location in locations:
1.7 - name, test, test_type, base, traversed, traversal_modes, attrnames, context, \
1.8 + name, test, test_type, base, traversed, traversal_modes, attrnames, \
1.9 + context, context_test, \
1.10 first_method, final_method, attr = self.access_plans[location]
1.11
1.12 print >>f_attrs, encode_access_location(location), \
1.13 @@ -408,7 +409,8 @@
1.14 ".".join(traversed) or "{}", \
1.15 ".".join(traversal_modes) or "{}", \
1.16 ".".join(attrnames) or "{}", \
1.17 - context, first_method, final_method, attr or "{}"
1.18 + context, context_test, \
1.19 + first_method, final_method, attr or "{}"
1.20
1.21 finally:
1.22 f_attrs.close()
1.23 @@ -1410,16 +1412,26 @@
1.24 'attrname' for type deduction.
1.25 """
1.26
1.27 + (class_types,
1.28 + only_instance_types,
1.29 + module_types) = self.get_types_for_attribute(attrname)
1.30 +
1.31 + self.init_reference_details(access_location)
1.32 +
1.33 + self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)
1.34 + self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)
1.35 +
1.36 + def get_types_for_attribute(self, attrname):
1.37 +
1.38 + "Return class, instance-only and module types supporting 'attrname'."
1.39 +
1.40 usage = ((attrname, False),)
1.41
1.42 class_types = self.get_class_types_for_usage(usage)
1.43 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types)
1.44 module_types = self.get_module_types_for_usage(usage)
1.45
1.46 - self.init_reference_details(access_location)
1.47 -
1.48 - self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False)
1.49 - self.record_reference_types(access_location, class_types, only_instance_types, module_types, False)
1.50 + return class_types, only_instance_types, module_types
1.51
1.52 def record_types_for_alias(self, accessor_location):
1.53
1.54 @@ -1761,6 +1773,7 @@
1.55 * access modes for each of the unambiguously-traversed attributes
1.56 * remaining attributes needing to be tested and traversed
1.57 * details of the context
1.58 + * any test to apply to the context
1.59 * the method of obtaining the final attribute
1.60 * any static final attribute
1.61 """
1.62 @@ -1855,19 +1868,25 @@
1.63 elif not base and ref:
1.64 dynamic_base = ref.get_origin()
1.65
1.66 + # Determine initial accessor details.
1.67 +
1.68 + accessor = base or dynamic_base
1.69 + accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None
1.70 + provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None
1.71 +
1.72 + # Traverse remaining attributes.
1.73 +
1.74 traversed = []
1.75 traversal_modes = []
1.76 - provider_kind = first(provider_kinds)
1.77 -
1.78 - # Traverse remaining attributes.
1.79
1.80 while len(attrs) == 1:
1.81 attr = first(attrs)
1.82 - accessor_kind = attr.get_kind()
1.83
1.84 traversed.append(attrname)
1.85 traversal_modes.append(accessor_kind == provider_kind and "object" or "class")
1.86
1.87 + # Consume attribute names providing unambiguous attributes.
1.88 +
1.89 del remaining[0]
1.90
1.91 if not remaining:
1.92 @@ -1880,11 +1899,18 @@
1.93 traversed = []
1.94 traversal_modes = []
1.95
1.96 - # Get the next attribute.
1.97 + # Get the access details.
1.98
1.99 attrname = remaining[0]
1.100 + accessor = attr.get_origin()
1.101 + accessor_kind = attr.get_kind()
1.102 + provider_kind = self.importer.get_attribute_provider(attr, attrname)
1.103 + accessor_kinds = [accessor_kind]
1.104 + provider_kinds = [provider_kind]
1.105 +
1.106 + # Get the next attribute.
1.107 +
1.108 attrs = self.importer.get_attributes(attr, attrname)
1.109 - provider_kind = self.importer.get_attribute_provider(attr, attrname)
1.110
1.111 # Where many attributes are suggested, no single attribute identity can
1.112 # be loaded.
1.113 @@ -1925,13 +1951,111 @@
1.114 first_method = "check" + (object_relative and "-object" or "") + \
1.115 (class_relative and "-class" or "")
1.116
1.117 + # Determine whether an unbound method is being accessed via an instance,
1.118 + # requiring a context test.
1.119 +
1.120 + context_test = "ignore"
1.121 +
1.122 + # Assignments do not employ the context.
1.123 +
1.124 + if is_assignment:
1.125 + pass
1.126 +
1.127 + # Obtain a selection of possible attributes if no unambiguous attribute
1.128 + # was identified.
1.129 +
1.130 + elif not attr:
1.131 +
1.132 + # Use previously-deduced attributes for a simple ambiguous access.
1.133 + # Otherwise, use the final attribute name to obtain possible
1.134 + # attributes.
1.135 +
1.136 + if len(remaining) > 1:
1.137 + attrname = remaining[-1]
1.138 +
1.139 + (class_types,
1.140 + only_instance_types,
1.141 + module_types) = self.get_types_for_attribute(attrname)
1.142 +
1.143 + all_accessor_kinds = set()
1.144 + all_provider_kinds = set()
1.145 +
1.146 + if class_types:
1.147 + all_accessor_kinds.add("<class>")
1.148 + all_accessor_kinds.add("<instance>")
1.149 + all_provider_kinds.add("<class>")
1.150 + if only_instance_types:
1.151 + all_accessor_kinds.add("<instance>")
1.152 + all_provider_kinds.add("<instance>")
1.153 + if module_types:
1.154 + all_accessor_kinds.add("<module>")
1.155 + all_provider_kinds.add("<module>")
1.156 +
1.157 + attrs = set()
1.158 + for type in combine_types(class_types, only_instance_types, module_types):
1.159 + attrs.update(self.importer.get_attributes(type, attrname))
1.160 +
1.161 + always_unbound = True
1.162 + have_function = False
1.163 + have_var = False
1.164 +
1.165 + # Determine whether all attributes are unbound methods and whether
1.166 + # functions or unidentified attributes occur.
1.167 +
1.168 + for attr in attrs:
1.169 + always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent()
1.170 + have_function = have_function or attr.has_kind("<function>")
1.171 + have_var = have_var or attr.has_kind("<var>")
1.172 +
1.173 + # Test for class-via-instance accesses.
1.174 +
1.175 + if accessor_kind == "<instance>" and \
1.176 + provider_kind == "<class>":
1.177 +
1.178 + if always_unbound:
1.179 + context_test = "replace"
1.180 + else:
1.181 + context_test = "test"
1.182 +
1.183 + # Test for the presence of class-via-instance accesses.
1.184 +
1.185 + elif "<instance>" in accessor_kinds and \
1.186 + "<class>" in provider_kinds and \
1.187 + (have_function or have_var):
1.188 +
1.189 + context_test = "test"
1.190 +
1.191 + # With an unambiguous attribute, determine whether a test is needed.
1.192 +
1.193 + elif accessor_kind == "<instance>" and \
1.194 + provider_kind == "<class>" and \
1.195 + (attr.has_kind("<var>") or
1.196 + attr.has_kind("<function>") and
1.197 + attr.name_parent() == attr.parent()):
1.198 +
1.199 + if attr.has_kind("<var>"):
1.200 + context_test = "test"
1.201 + else:
1.202 + context_test = "replace"
1.203 +
1.204 + # With an unambiguous attribute with ambiguity in the access method,
1.205 + # generate a test.
1.206 +
1.207 + elif "<instance>" in accessor_kinds and \
1.208 + "<class>" in provider_kinds and \
1.209 + (attr.has_kind("<var>") or
1.210 + attr.has_kind("<function>") and
1.211 + attr.name_parent() == attr.parent()):
1.212 +
1.213 + context_test = "test"
1.214 +
1.215 # Determine the nature of the context.
1.216
1.217 - context = is_assignment and "unset" or \
1.218 + context = context_test == "ignore" and "unset" or \
1.219 len(traversed + remaining) == 1 and \
1.220 (base and "base" or "original-accessor") or \
1.221 "final-accessor"
1.222
1.223 - return name, test, test_type, base, traversed, traversal_modes, remaining, context, first_method, final_method, origin
1.224 + return name, test, test_type, base, traversed, traversal_modes, remaining, context, context_test, first_method, final_method, origin
1.225
1.226 # vim: tabstop=4 expandtab shiftwidth=4
2.1 --- a/optimiser.py Sat Oct 15 23:32:15 2016 +0200
2.2 +++ b/optimiser.py Sat Oct 15 23:34:29 2016 +0200
2.3 @@ -344,7 +344,7 @@
2.4 # Obtain the access details.
2.5
2.6 name, test, test_type, base, traversed, traversal_modes, \
2.7 - attrnames, context, first_method, final_method, origin = access_plan
2.8 + attrnames, context, context_test, first_method, final_method, origin = access_plan
2.9
2.10 instructions = []
2.11 emit = instructions.append
2.12 @@ -383,7 +383,7 @@
2.13
2.14 # Assigning does not set the context.
2.15
2.16 - elif context in ("final-accessor", "unset"):
2.17 + elif context in ("final-accessor", "unset") and access_first_attribute:
2.18 emit(("set_accessor", original_accessor))
2.19 accessor = "accessor"
2.20
2.21 @@ -405,6 +405,8 @@
2.22 # Perform the first or final access.
2.23 # The access only needs performing if the resulting accessor is used.
2.24
2.25 + remaining = len(traversed + attrnames)
2.26 +
2.27 if access_first_attribute:
2.28
2.29 if first_method == "relative-class":
2.30 @@ -443,9 +445,7 @@
2.31 else:
2.32 emit(("set_accessor", ("check_and_load_via_any", accessor, attrname)))
2.33
2.34 - # Obtain an accessor.
2.35 -
2.36 - remaining = len(traversed + attrnames)
2.37 + # Traverse attributes using the accessor.
2.38
2.39 if traversed:
2.40 for attrname, traversal_mode in zip(traversed, traversal_modes):
2.41 @@ -496,7 +496,14 @@
2.42 if final_method == "static-assign":
2.43 emit(("store_member", origin, "<assexpr>"))
2.44 elif final_method == "static":
2.45 - emit(("load_static", origin))
2.46 + emit(("set_accessor", ("load_static", origin)))
2.47 +
2.48 + if context_test == "test":
2.49 + emit(("test_context", "context", "accessor"))
2.50 + elif context_test == "replace":
2.51 + emit(("replace_context", "context", "accessor"))
2.52 + else:
2.53 + emit(("get_accessor",))
2.54
2.55 self.access_instructions[access_location] = instructions
2.56