# HG changeset patch # User Paul Boddie # Date 1476567269 -7200 # Node ID 7de3090fed03212edd80f49c1ad521ede9a5bafc # Parent be308b898916fea05e8a167b58bf8bf761b32e58 Refined accessor and provider computation in order to provide context testing information. Record access plan cases where any context will be ignored. diff -r be308b898916 -r 7de3090fed03 deducer.py --- a/deducer.py Sat Oct 15 23:32:15 2016 +0200 +++ b/deducer.py Sat Oct 15 23:34:29 2016 +0200 @@ -399,7 +399,8 @@ locations.sort() for location in locations: - name, test, test_type, base, traversed, traversal_modes, attrnames, context, \ + name, test, test_type, base, traversed, traversal_modes, attrnames, \ + context, context_test, \ first_method, final_method, attr = self.access_plans[location] print >>f_attrs, encode_access_location(location), \ @@ -408,7 +409,8 @@ ".".join(traversed) or "{}", \ ".".join(traversal_modes) or "{}", \ ".".join(attrnames) or "{}", \ - context, first_method, final_method, attr or "{}" + context, context_test, \ + first_method, final_method, attr or "{}" finally: f_attrs.close() @@ -1410,16 +1412,26 @@ 'attrname' for type deduction. """ + (class_types, + only_instance_types, + module_types) = self.get_types_for_attribute(attrname) + + self.init_reference_details(access_location) + + self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False) + self.record_reference_types(access_location, class_types, only_instance_types, module_types, False) + + def get_types_for_attribute(self, attrname): + + "Return class, instance-only and module types supporting 'attrname'." + usage = ((attrname, False),) class_types = self.get_class_types_for_usage(usage) only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) module_types = self.get_module_types_for_usage(usage) - self.init_reference_details(access_location) - - self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False) - self.record_reference_types(access_location, class_types, only_instance_types, module_types, False) + return class_types, only_instance_types, module_types def record_types_for_alias(self, accessor_location): @@ -1761,6 +1773,7 @@ * access modes for each of the unambiguously-traversed attributes * remaining attributes needing to be tested and traversed * details of the context + * any test to apply to the context * the method of obtaining the final attribute * any static final attribute """ @@ -1855,19 +1868,25 @@ elif not base and ref: dynamic_base = ref.get_origin() + # Determine initial accessor details. + + accessor = base or dynamic_base + accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None + provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None + + # Traverse remaining attributes. + traversed = [] traversal_modes = [] - provider_kind = first(provider_kinds) - - # Traverse remaining attributes. while len(attrs) == 1: attr = first(attrs) - accessor_kind = attr.get_kind() traversed.append(attrname) traversal_modes.append(accessor_kind == provider_kind and "object" or "class") + # Consume attribute names providing unambiguous attributes. + del remaining[0] if not remaining: @@ -1880,11 +1899,18 @@ traversed = [] traversal_modes = [] - # Get the next attribute. + # Get the access details. attrname = remaining[0] + accessor = attr.get_origin() + accessor_kind = attr.get_kind() + provider_kind = self.importer.get_attribute_provider(attr, attrname) + accessor_kinds = [accessor_kind] + provider_kinds = [provider_kind] + + # Get the next attribute. + attrs = self.importer.get_attributes(attr, attrname) - provider_kind = self.importer.get_attribute_provider(attr, attrname) # Where many attributes are suggested, no single attribute identity can # be loaded. @@ -1925,13 +1951,111 @@ first_method = "check" + (object_relative and "-object" or "") + \ (class_relative and "-class" or "") + # Determine whether an unbound method is being accessed via an instance, + # requiring a context test. + + context_test = "ignore" + + # Assignments do not employ the context. + + if is_assignment: + pass + + # Obtain a selection of possible attributes if no unambiguous attribute + # was identified. + + elif not attr: + + # Use previously-deduced attributes for a simple ambiguous access. + # Otherwise, use the final attribute name to obtain possible + # attributes. + + if len(remaining) > 1: + attrname = remaining[-1] + + (class_types, + only_instance_types, + module_types) = self.get_types_for_attribute(attrname) + + all_accessor_kinds = set() + all_provider_kinds = set() + + if class_types: + all_accessor_kinds.add("") + all_accessor_kinds.add("") + all_provider_kinds.add("") + if only_instance_types: + all_accessor_kinds.add("") + all_provider_kinds.add("") + if module_types: + all_accessor_kinds.add("") + all_provider_kinds.add("") + + attrs = set() + for type in combine_types(class_types, only_instance_types, module_types): + attrs.update(self.importer.get_attributes(type, attrname)) + + always_unbound = True + have_function = False + have_var = False + + # Determine whether all attributes are unbound methods and whether + # functions or unidentified attributes occur. + + for attr in attrs: + always_unbound = always_unbound and attr.has_kind("") and attr.name_parent() == attr.parent() + have_function = have_function or attr.has_kind("") + have_var = have_var or attr.has_kind("") + + # Test for class-via-instance accesses. + + if accessor_kind == "" and \ + provider_kind == "": + + if always_unbound: + context_test = "replace" + else: + context_test = "test" + + # Test for the presence of class-via-instance accesses. + + elif "" in accessor_kinds and \ + "" in provider_kinds and \ + (have_function or have_var): + + context_test = "test" + + # With an unambiguous attribute, determine whether a test is needed. + + elif accessor_kind == "" and \ + provider_kind == "" and \ + (attr.has_kind("") or + attr.has_kind("") and + attr.name_parent() == attr.parent()): + + if attr.has_kind(""): + context_test = "test" + else: + context_test = "replace" + + # With an unambiguous attribute with ambiguity in the access method, + # generate a test. + + elif "" in accessor_kinds and \ + "" in provider_kinds and \ + (attr.has_kind("") or + attr.has_kind("") and + attr.name_parent() == attr.parent()): + + context_test = "test" + # Determine the nature of the context. - context = is_assignment and "unset" or \ + context = context_test == "ignore" and "unset" or \ len(traversed + remaining) == 1 and \ (base and "base" or "original-accessor") or \ "final-accessor" - return name, test, test_type, base, traversed, traversal_modes, remaining, context, first_method, final_method, origin + return name, test, test_type, base, traversed, traversal_modes, remaining, context, context_test, first_method, final_method, origin # vim: tabstop=4 expandtab shiftwidth=4 diff -r be308b898916 -r 7de3090fed03 optimiser.py --- a/optimiser.py Sat Oct 15 23:32:15 2016 +0200 +++ b/optimiser.py Sat Oct 15 23:34:29 2016 +0200 @@ -344,7 +344,7 @@ # Obtain the access details. name, test, test_type, base, traversed, traversal_modes, \ - attrnames, context, first_method, final_method, origin = access_plan + attrnames, context, context_test, first_method, final_method, origin = access_plan instructions = [] emit = instructions.append @@ -383,7 +383,7 @@ # Assigning does not set the context. - elif context in ("final-accessor", "unset"): + elif context in ("final-accessor", "unset") and access_first_attribute: emit(("set_accessor", original_accessor)) accessor = "accessor" @@ -405,6 +405,8 @@ # Perform the first or final access. # The access only needs performing if the resulting accessor is used. + remaining = len(traversed + attrnames) + if access_first_attribute: if first_method == "relative-class": @@ -443,9 +445,7 @@ else: emit(("set_accessor", ("check_and_load_via_any", accessor, attrname))) - # Obtain an accessor. - - remaining = len(traversed + attrnames) + # Traverse attributes using the accessor. if traversed: for attrname, traversal_mode in zip(traversed, traversal_modes): @@ -496,7 +496,14 @@ if final_method == "static-assign": emit(("store_member", origin, "")) elif final_method == "static": - emit(("load_static", origin)) + emit(("set_accessor", ("load_static", origin))) + + if context_test == "test": + emit(("test_context", "context", "accessor")) + elif context_test == "replace": + emit(("replace_context", "context", "accessor")) + else: + emit(("get_accessor",)) self.access_instructions[access_location] = instructions