1.1 --- a/common.py Sat Mar 11 01:04:34 2017 +0100
1.2 +++ b/common.py Mon Mar 13 17:53:19 2017 +0100
1.3 @@ -129,7 +129,6 @@
1.4
1.5 self.astnode = None
1.6 self.encoding = None
1.7 - self.iterators = {}
1.8 self.temp = {}
1.9 self.lambdas = {}
1.10
1.11 @@ -230,19 +229,6 @@
1.12 def next_literal(self):
1.13 self.literals[self.get_namespace_path()] += 1
1.14
1.15 - # Temporary iterator naming.
1.16 -
1.17 - def get_iterator_path(self):
1.18 - return self.in_function and self.get_namespace_path() or self.name
1.19 -
1.20 - def get_iterator_name(self):
1.21 - path = self.get_iterator_path()
1.22 - init_item(self.iterators, path, lambda: 0)
1.23 - return "$i%d" % self.iterators[path]
1.24 -
1.25 - def next_iterator(self):
1.26 - self.iterators[self.get_iterator_path()] += 1
1.27 -
1.28 # Temporary variable naming.
1.29
1.30 def get_temporary_name(self):
1.31 @@ -565,17 +551,32 @@
1.32 the iterator, producing a replacement node for the original.
1.33 """
1.34
1.35 + t0 = self.get_temporary_name()
1.36 + self.next_temporary()
1.37 + t1 = self.get_temporary_name()
1.38 + self.next_temporary()
1.39 + i0 = self.get_temporary_name()
1.40 + self.next_temporary()
1.41 +
1.42 node = compiler.ast.Stmt([
1.43
1.44 - # <next> = {n.list}.__iter__().next
1.45 + # <t0> = {n.list}
1.46 + # <t1> = <t0>.__iter__()
1.47 + # <i0> = <t1>.next
1.48
1.49 compiler.ast.Assign(
1.50 - [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")],
1.51 - compiler.ast.Getattr(
1.52 - compiler.ast.CallFunc(
1.53 - compiler.ast.Getattr(n.list, "__iter__"),
1.54 - []
1.55 - ), "next")),
1.56 + [compiler.ast.AssName(t0, "OP_ASSIGN")],
1.57 + n.list),
1.58 +
1.59 + compiler.ast.Assign(
1.60 + [compiler.ast.AssName(t1, "OP_ASSIGN")],
1.61 + compiler.ast.CallFunc(
1.62 + compiler.ast.Getattr(compiler.ast.Name(t0), "__iter__"),
1.63 + [])),
1.64 +
1.65 + compiler.ast.Assign(
1.66 + [compiler.ast.AssName(i0, "OP_ASSIGN")],
1.67 + compiler.ast.Getattr(compiler.ast.Name(t1), "next")),
1.68
1.69 # try:
1.70 # while True:
1.71 @@ -591,7 +592,7 @@
1.72 compiler.ast.Assign(
1.73 [n.assign],
1.74 compiler.ast.CallFunc(
1.75 - compiler.ast.Name(self.get_iterator_name()),
1.76 + compiler.ast.Name(i0),
1.77 []
1.78 )),
1.79 n.body]),
1.80 @@ -600,7 +601,6 @@
1.81 None)
1.82 ])
1.83
1.84 - self.next_iterator()
1.85 self.process_structure_node(node)
1.86
1.87 def process_literal_sequence_node(self, n, name, ref, cls):
1.88 @@ -987,6 +987,185 @@
1.89 def same(s1, s2):
1.90 return set(s1) == set(s2)
1.91
1.92 +def order_dependencies(all_depends):
1.93 +
1.94 + """
1.95 + Produce a dependency ordering for the 'all_depends' mapping. This mapping
1.96 + has the form "A depends on B, C...". The result will order A, B, C, and so
1.97 + on.
1.98 + """
1.99 +
1.100 + usage = init_reverse_dependencies(all_depends)
1.101 +
1.102 + # Produce an ordering by obtaining exposed items (required by items already
1.103 + # processed) and putting them at the start of the list.
1.104 +
1.105 + ordered = []
1.106 +
1.107 + while usage:
1.108 + have_next = False
1.109 +
1.110 + for key, n in usage.items():
1.111 +
1.112 + # Add items needed by no other items to the ordering.
1.113 +
1.114 + if not n:
1.115 + remove_dependency(key, all_depends, usage, ordered)
1.116 + have_next = True
1.117 +
1.118 + if not have_next:
1.119 + raise ValueError, usage
1.120 +
1.121 + return ordered
1.122 +
1.123 +def order_dependencies_partial(all_depends):
1.124 +
1.125 + """
1.126 + Produce a dependency ordering for the 'all_depends' mapping. This mapping
1.127 + has the form "A depends on B, C...". The result will order A, B, C, and so
1.128 + on. Where cycles exist, they will be broken and a partial ordering returned.
1.129 + """
1.130 +
1.131 + usage = init_reverse_dependencies(all_depends)
1.132 +
1.133 + # Duplicate the dependencies for subsequent modification.
1.134 +
1.135 + new_depends = {}
1.136 + for key, values in all_depends.items():
1.137 + new_depends[key] = set(values)
1.138 +
1.139 + all_depends = new_depends
1.140 +
1.141 + # Produce an ordering by obtaining exposed items (required by items already
1.142 + # processed) and putting them at the start of the list.
1.143 +
1.144 + ordered = []
1.145 +
1.146 + while usage:
1.147 + least = None
1.148 + least_key = None
1.149 +
1.150 + for key, n in usage.items():
1.151 +
1.152 + # Add items needed by no other items to the ordering.
1.153 +
1.154 + if not n:
1.155 + remove_dependency(key, all_depends, usage, ordered)
1.156 + least = 0
1.157 +
1.158 + # When breaking cycles, note the least used items.
1.159 +
1.160 + elif least is None or len(n) < least:
1.161 + least_key = key
1.162 + least = len(n)
1.163 +
1.164 + if least:
1.165 + transfer_dependencies(least_key, all_depends, usage, ordered)
1.166 +
1.167 + return ordered
1.168 +
1.169 +def init_reverse_dependencies(all_depends):
1.170 +
1.171 + """
1.172 + From 'all_depends', providing a mapping of the form "A depends on B, C...",
1.173 + record the reverse dependencies, making a mapping of the form
1.174 + "B is needed by A", "C is needed by A", and so on.
1.175 + """
1.176 +
1.177 + usage = {}
1.178 +
1.179 + # Record path-based dependencies.
1.180 +
1.181 + for key in all_depends.keys():
1.182 + usage[key] = set()
1.183 +
1.184 + for key, depends in all_depends.items():
1.185 + for depend in depends:
1.186 + init_item(usage, depend, set)
1.187 + usage[depend].add(key)
1.188 +
1.189 + return usage
1.190 +
1.191 +def transfer_dependencies(key, all_depends, usage, ordered):
1.192 +
1.193 + """
1.194 + Transfer items needed by 'key' to those items needing 'key', found using
1.195 + 'all_depends', and updating 'usage'. Insert 'key' into the 'ordered'
1.196 + collection of dependencies.
1.197 +
1.198 + If "A is needed by X" and "B is needed by A", then transferring items needed
1.199 + by A will cause "B is needed by X" to be recorded as a consequence.
1.200 +
1.201 + Transferring items also needs to occur in the reverse mapping, so that
1.202 + "A needs B" and "X needs A", then the consequence must be recorded as
1.203 + "X needs B".
1.204 + """
1.205 +
1.206 + ordered.insert(0, key)
1.207 +
1.208 + needing = usage[key] # A is needed by X
1.209 + needed = all_depends.get(key) # A needs B
1.210 +
1.211 + if needing:
1.212 + for depend in needing:
1.213 + l = all_depends.get(depend)
1.214 + if not l:
1.215 + continue
1.216 +
1.217 + l.remove(key) # X needs (A)
1.218 +
1.219 + if needed:
1.220 + l.update(needed) # X needs B...
1.221 +
1.222 + # Prevent self references.
1.223 +
1.224 + if depend in needed:
1.225 + l.remove(depend)
1.226 +
1.227 + if needed:
1.228 + for depend in needed:
1.229 + l = usage.get(depend)
1.230 + if not l:
1.231 + continue
1.232 +
1.233 + l.remove(key) # B is needed by (A)
1.234 + l.update(needing) # B is needed by X...
1.235 +
1.236 + # Prevent self references.
1.237 +
1.238 + if depend in needing:
1.239 + l.remove(depend)
1.240 +
1.241 + if needed:
1.242 + del all_depends[key]
1.243 + del usage[key]
1.244 +
1.245 +def remove_dependency(key, all_depends, usage, ordered):
1.246 +
1.247 + """
1.248 + Remove 'key', found in 'all_depends', from 'usage', inserting it into the
1.249 + 'ordered' collection of dependencies.
1.250 +
1.251 + Given that 'usage' for a given key A would indicate that "A needs <nothing>"
1.252 + upon removing A from 'usage', the outcome is that all keys needing A will
1.253 + have A removed from their 'usage' records.
1.254 +
1.255 + So, if "B needs A", removing A will cause "B needs <nothing>" to be recorded
1.256 + as a consequence.
1.257 + """
1.258 +
1.259 + ordered.insert(0, key)
1.260 +
1.261 + depends = all_depends.get(key)
1.262 +
1.263 + # Reduce usage of the referenced items.
1.264 +
1.265 + if depends:
1.266 + for depend in depends:
1.267 + usage[depend].remove(key)
1.268 +
1.269 + del usage[key]
1.270 +
1.271 # General input/output.
1.272
1.273 def readfile(filename):
2.1 --- a/deducer.py Sat Mar 11 01:04:34 2017 +0100
2.2 +++ b/deducer.py Mon Mar 13 17:53:19 2017 +0100
2.3 @@ -22,7 +22,7 @@
2.4 from common import first, get_assigned_attributes, \
2.5 get_attrname_from_location, get_attrnames, \
2.6 get_invoked_attributes, get_name_path, init_item, \
2.7 - sorted_output, CommonOutput
2.8 + order_dependencies_partial, sorted_output, CommonOutput
2.9 from encoders import encode_access_location, encode_constrained, \
2.10 encode_instruction, encode_location, encode_usage, \
2.11 get_kinds, test_label_for_kind, test_label_for_type
2.12 @@ -146,6 +146,7 @@
2.13 self.init_accessors()
2.14 self.init_accesses()
2.15 self.init_aliases()
2.16 + self.init_alias_network()
2.17 self.modify_mutated_attributes()
2.18 self.identify_references()
2.19 self.classify_accessors()
2.20 @@ -903,9 +904,11 @@
2.21 access_location = (path, None, attrname_str, 0)
2.22
2.23 # Plain name accesses do not employ attributes and are
2.24 - # ignored.
2.25 + # ignored. Whether they are invoked is of interest, however.
2.26
2.27 if not attrname_str:
2.28 + if invocation:
2.29 + self.reference_invocations[access_location] = invocation
2.30 continue
2.31
2.32 attrnames = get_attrnames(attrname_str)
2.33 @@ -1016,6 +1019,21 @@
2.34 self.alias_index[accessor_location] = updated_locations
2.35 return updated_locations
2.36
2.37 + def init_alias_network(self):
2.38 +
2.39 + """
2.40 + Initialise a network of aliases, their initialising accesses, and the
2.41 + accessors supporting those accesses.
2.42 + """
2.43 +
2.44 + self.alias_network = {}
2.45 + self.alias_network.update(self.alias_index)
2.46 +
2.47 + for accessor_location, access_locations in self.alias_index.items():
2.48 + for access_location in access_locations:
2.49 + if not self.alias_network.has_key(access_location):
2.50 + self.alias_network[access_location] = self.get_accessors_for_access(access_location)
2.51 +
2.52 # Attribute mutation for types.
2.53
2.54 def modify_mutated_attributes(self):
2.55 @@ -1350,8 +1368,9 @@
2.56 # Aliased name definitions. All aliases with usage will have been
2.57 # defined, but they may be refined according to referenced accesses.
2.58
2.59 - for accessor_location in self.alias_index.keys():
2.60 - self.record_types_for_alias(accessor_location)
2.61 + for location in order_dependencies_partial(self.alias_network):
2.62 + if self.alias_index.has_key(location):
2.63 + self.record_types_for_alias(location)
2.64
2.65 # Update accesses employing aliases.
2.66
2.67 @@ -1518,6 +1537,9 @@
2.68 if not attrname:
2.69 return
2.70
2.71 + invocation = access_location in self.reference_invocations
2.72 + assignment = access_location in self.reference_assignments
2.73 +
2.74 # Collect all suggested types for the accessors. Accesses may
2.75 # require accessors from of a subset of the complete set of types.
2.76
2.77 @@ -1547,7 +1569,7 @@
2.78
2.79 else:
2.80 self.init_definition_details(location)
2.81 - self.record_types_for_usage(location, [(attrname, False, False)])
2.82 + self.record_types_for_usage(location, [(attrname, invocation, assignment)])
2.83
2.84 constrained = location in self.accessor_constrained and constrained
2.85
2.86 @@ -1609,36 +1631,64 @@
2.87 have_access = self.provider_class_types.has_key(accessor_location)
2.88
2.89 # With an access, attempt to narrow the existing selection of provider
2.90 - # types.
2.91 + # types. Invocations attempt to find return value information, with
2.92 + # instance return values also yielding class providers (since attributes
2.93 + # on instances could be provided by classes).
2.94
2.95 if have_access:
2.96 provider_class_types = self.provider_class_types[accessor_location]
2.97 provider_instance_types = self.provider_instance_types[accessor_location]
2.98 provider_module_types = self.provider_module_types[accessor_location]
2.99
2.100 + accessor_class_types = self.accessor_class_types[accessor_location]
2.101 + accessor_instance_types = self.accessor_instance_types[accessor_location]
2.102 + accessor_module_types = self.accessor_module_types[accessor_location]
2.103 +
2.104 # Find details for any corresponding access.
2.105
2.106 - all_class_types = set()
2.107 - all_instance_types = set()
2.108 - all_module_types = set()
2.109 + new_provider_class_types = set()
2.110 + new_provider_instance_types = set()
2.111 + new_provider_module_types = set()
2.112 +
2.113 + new_accessor_class_types = set()
2.114 + new_accessor_instance_types = set()
2.115 + new_accessor_module_types = set()
2.116
2.117 for access_location in self.alias_index[accessor_location]:
2.118 location, name, attrnames, access_number = access_location
2.119 + invocation = self.reference_invocations.get(access_location)
2.120 +
2.121 + attrnames = attrnames and attrnames.split(".")
2.122 + remaining = attrnames and len(attrnames) > 1
2.123 +
2.124 + # Alias has remaining attributes: reference details do not
2.125 + # correspond to the accessor; the remaining attributes would
2.126 + # need to be traversed first.
2.127 +
2.128 + if remaining:
2.129 + return
2.130
2.131 # Alias references an attribute access.
2.132
2.133 if attrnames:
2.134
2.135 - # Obtain attribute references for the access.
2.136 -
2.137 - attrs = []
2.138 - for _attrtype, object_type, attr in self.referenced_attrs[access_location]:
2.139 - attrs.append(attr)
2.140 -
2.141 - # Separate the different attribute types.
2.142 -
2.143 - (class_types, instance_types, module_types,
2.144 - function_types, var_types) = separate_types(attrs)
2.145 + # Obtain references and attribute types for the access.
2.146 +
2.147 + attrs = self.get_references_for_access(access_location)
2.148 +
2.149 + # Where no specific attributes are defined, do not attempt
2.150 + # to refine the alias's types.
2.151 +
2.152 + if not attrs:
2.153 + return
2.154 +
2.155 + # Invocations converting class accessors to instances do not
2.156 + # change the nature of class providers.
2.157 +
2.158 + provider_attrs = self.convert_invocation_providers(attrs, invocation)
2.159 +
2.160 + (class_types, instance_types, module_types, function_types,
2.161 + var_types) = separate_types(provider_attrs)
2.162
2.163 # Where non-accessor types are found, do not attempt to refine
2.164 # the defined accessor types.
2.165 @@ -1650,6 +1700,26 @@
2.166 instance_types = set(provider_instance_types).intersection(instance_types)
2.167 module_types = set(provider_module_types).intersection(module_types)
2.168
2.169 + new_provider_class_types.update(class_types)
2.170 + new_provider_instance_types.update(instance_types)
2.171 + new_provider_module_types.update(module_types)
2.172 +
2.173 + # Accessors are updated separately, employing invocation
2.174 + # result details.
2.175 +
2.176 + accessor_attrs = self.convert_invocations(attrs, invocation)
2.177 +
2.178 + (class_types, instance_types, module_types, function_types,
2.179 + var_types) = separate_types(accessor_attrs)
2.180 +
2.181 + class_types = set(accessor_class_types).intersection(class_types)
2.182 + instance_types = set(accessor_instance_types).intersection(instance_types)
2.183 + module_types = set(accessor_module_types).intersection(module_types)
2.184 +
2.185 + new_accessor_class_types.update(class_types)
2.186 + new_accessor_instance_types.update(instance_types)
2.187 + new_accessor_module_types.update(module_types)
2.188 +
2.189 # Alias references a name, not an access.
2.190
2.191 else:
2.192 @@ -1657,8 +1727,32 @@
2.193
2.194 attr = self.get_initialised_name(access_location)
2.195 if attr:
2.196 - (class_types, instance_types, module_types,
2.197 - _function_types, _var_types) = separate_types([attr])
2.198 + attrs = [attr]
2.199 + provider_attrs = self.convert_invocation_providers(attrs, invocation)
2.200 +
2.201 + (class_types, instance_types, module_types, function_types,
2.202 + var_types) = separate_types(provider_attrs)
2.203 +
2.204 + class_types = set(provider_class_types).intersection(class_types)
2.205 + instance_types = set(provider_instance_types).intersection(instance_types)
2.206 + module_types = set(provider_module_types).intersection(module_types)
2.207 +
2.208 + new_provider_class_types.update(class_types)
2.209 + new_provider_instance_types.update(instance_types)
2.210 + new_provider_module_types.update(module_types)
2.211 +
2.212 + accessor_attrs = self.convert_invocations(attrs, invocation)
2.213 +
2.214 + (class_types, instance_types, module_types, function_types,
2.215 + var_types) = separate_types(accessor_attrs)
2.216 +
2.217 + class_types = set(accessor_class_types).intersection(class_types)
2.218 + instance_types = set(accessor_instance_types).intersection(instance_types)
2.219 + module_types = set(accessor_module_types).intersection(module_types)
2.220 +
2.221 + new_accessor_class_types.update(class_types)
2.222 + new_accessor_instance_types.update(instance_types)
2.223 + new_accessor_module_types.update(module_types)
2.224
2.225 # Where no further information is found, do not attempt to
2.226 # refine the defined accessor types.
2.227 @@ -1666,16 +1760,15 @@
2.228 else:
2.229 return
2.230
2.231 - all_class_types.update(class_types)
2.232 - all_instance_types.update(instance_types)
2.233 - all_module_types.update(module_types)
2.234 -
2.235 # Record refined type details for the alias as an accessor.
2.236
2.237 self.init_definition_details(accessor_location)
2.238 - self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False)
2.239 + self.update_provider_types(accessor_location, new_provider_class_types, new_provider_instance_types, new_provider_module_types)
2.240 + self.update_accessor_types(accessor_location, new_accessor_class_types, new_accessor_instance_types, new_accessor_module_types)
2.241
2.242 # Without an access, attempt to identify references for the alias.
2.243 + # Invocations convert classes to instances and also attempt to find
2.244 + # return value information.
2.245
2.246 else:
2.247 refs = set()
2.248 @@ -1688,6 +1781,8 @@
2.249 access_location = self.const_accesses[access_location]
2.250
2.251 location, name, attrnames, access_number = access_location
2.252 + invocation = self.reference_invocations.get(access_location)
2.253 +
2.254 attrnames = attrnames and attrnames.split(".")
2.255 remaining = attrnames and len(attrnames) > 1
2.256
2.257 @@ -1700,31 +1795,94 @@
2.258
2.259 # Alias references an attribute access.
2.260
2.261 - attrname = attrnames and attrnames[0]
2.262 -
2.263 - if attrname:
2.264 - attrs = []
2.265 - for attrtype, object_type, attr in self.referenced_attrs[access_location]:
2.266 - attrs.append(attr)
2.267 + if attrnames:
2.268 +
2.269 + # Obtain references and attribute types for the access.
2.270 +
2.271 + attrs = self.get_references_for_access(access_location)
2.272 + attrs = self.convert_invocations(attrs, invocation)
2.273 refs.update(attrs)
2.274
2.275 # Alias references a name, not an access.
2.276
2.277 else:
2.278 +
2.279 + # Obtain initialiser information.
2.280 +
2.281 attr = self.get_initialised_name(access_location)
2.282 - attrs = attr and [attr] or []
2.283 - if not attrs and self.provider_class_types.has_key(access_location):
2.284 + if attr:
2.285 + refs.update(self.convert_invocations([attr], invocation))
2.286 +
2.287 + # Obtain provider information.
2.288 +
2.289 + elif self.provider_class_types.has_key(access_location):
2.290 class_types = self.provider_class_types[access_location]
2.291 instance_types = self.provider_instance_types[access_location]
2.292 module_types = self.provider_module_types[access_location]
2.293 - attrs = combine_types(class_types, instance_types, module_types)
2.294 - if attrs:
2.295 - refs.update(attrs)
2.296 +
2.297 + types = combine_types(class_types, instance_types, module_types)
2.298 + refs.update(self.convert_invocation_providers(types, invocation))
2.299
2.300 # Record reference details for the alias separately from accessors.
2.301
2.302 self.referenced_objects[accessor_location] = refs
2.303
2.304 + def get_references_for_access(self, access_location):
2.305 +
2.306 + "Return the references identified for 'access_location'."
2.307 +
2.308 + attrs = []
2.309 + for attrtype, object_type, attr in self.referenced_attrs[access_location]:
2.310 + attrs.append(attr)
2.311 + return attrs
2.312 +
2.313 + def convert_invocation_providers(self, refs, invocation):
2.314 +
2.315 + """
2.316 + Convert 'refs' to providers corresponding to the results of invoking
2.317 + each of the given references, if 'invocation' is set to a true value.
2.318 + """
2.319 +
2.320 + if not invocation:
2.321 + return refs
2.322 +
2.323 + providers = set()
2.324 +
2.325 + for ref in refs:
2.326 + ref = self.convert_invocation_provider(ref)
2.327 + if ref.has_kind("<instance>"):
2.328 + providers.add(Reference("<class>", ref.get_origin()))
2.329 + providers.add(ref)
2.330 +
2.331 + return providers
2.332 +
2.333 + def convert_invocation_provider(self, ref):
2.334 +
2.335 + "Convert 'ref' to a provider appropriate to its invocation result."
2.336 +
2.337 + if ref and ref.has_kind("<class>"):
2.338 + return ref
2.339 +
2.340 + return Reference("<var>")
2.341 +
2.342 + def convert_invocations(self, refs, invocation):
2.343 +
2.344 + """
2.345 + Convert 'refs' to invocation results if 'invocation' is set to a true
2.346 + value.
2.347 + """
2.348 +
2.349 + return invocation and map(self.convert_invocation, refs) or refs
2.350 +
2.351 + def convert_invocation(self, ref):
2.352 +
2.353 + "Convert 'ref' to its invocation result."
2.354 +
2.355 + if ref and ref.has_kind("<class>"):
2.356 + return ref.instance_of()
2.357 +
2.358 + return Reference("<var>")
2.359 +
2.360 def get_initialised_name(self, access_location):
2.361
2.362 """
2.363 @@ -1770,15 +1928,13 @@
2.364
2.365 # Update the type details for the location.
2.366
2.367 - self.provider_class_types[location].update(class_types)
2.368 - self.provider_instance_types[location].update(instance_types)
2.369 - self.provider_module_types[location].update(module_types)
2.370 + self.update_provider_types(location, class_types, instance_types, module_types)
2.371
2.372 # Class types support classes and instances as accessors.
2.373 # Instance-only and module types support only their own kinds as
2.374 # accessors.
2.375
2.376 - path, name, version, attrnames = location
2.377 + path, name, attrnames, version = location
2.378
2.379 if invocations:
2.380 class_only_types = self.filter_for_invocations(class_types, invocations)
2.381 @@ -1802,6 +1958,28 @@
2.382 if constrained:
2.383 self.accessor_constrained.add(location)
2.384
2.385 + def update_provider_types(self, location, class_types, instance_types, module_types):
2.386 +
2.387 + """
2.388 + Update provider types for the given 'location', adding 'class_types',
2.389 + 'instance_types' and 'module_types' to those already stored.
2.390 + """
2.391 +
2.392 + self.provider_class_types[location].update(class_types)
2.393 + self.provider_instance_types[location].update(instance_types)
2.394 + self.provider_module_types[location].update(module_types)
2.395 +
2.396 + def update_accessor_types(self, location, class_types, instance_types, module_types):
2.397 +
2.398 + """
2.399 + Update accessor types for the given 'location', adding 'class_types',
2.400 + 'instance_types' and 'module_types' to those already stored.
2.401 + """
2.402 +
2.403 + self.accessor_class_types[location].update(class_types)
2.404 + self.accessor_instance_types[location].update(instance_types)
2.405 + self.accessor_module_types[location].update(module_types)
2.406 +
2.407 def filter_for_invocations(self, class_types, attrnames):
2.408
2.409 """
3.1 --- a/importer.py Sat Mar 11 01:04:34 2017 +0100
3.2 +++ b/importer.py Mon Mar 13 17:53:19 2017 +0100
3.3 @@ -23,7 +23,7 @@
3.4 from errors import ProgramError
3.5 from os.path import exists, extsep, getmtime, join
3.6 from os import listdir, makedirs, remove
3.7 -from common import init_item, readfile, writefile
3.8 +from common import init_item, order_dependencies, readfile, writefile
3.9 from modules import CachedModule
3.10 from referencing import Reference
3.11 import inspector
3.12 @@ -673,44 +673,10 @@
3.13
3.14 self.condense_dependencies()
3.15
3.16 - # Record the number of modules using or depending on each module.
3.17 -
3.18 - usage = {}
3.19 -
3.20 - # Record path-based dependencies.
3.21 -
3.22 - for path in self.depends.keys():
3.23 - usage[path] = set()
3.24 -
3.25 - for path, depends in self.depends.items():
3.26 - for origin in depends:
3.27 - init_item(usage, origin, set)
3.28 - usage[origin].add(path)
3.29 -
3.30 - # Produce an ordering by obtaining exposed modules (required by modules
3.31 - # already processed) and putting them at the start of the list.
3.32 -
3.33 - ordered = []
3.34 -
3.35 - while usage:
3.36 - have_next = False
3.37 -
3.38 - for path, n in usage.items():
3.39 - if not n:
3.40 - ordered.insert(0, path)
3.41 - depends = self.depends.get(path)
3.42 -
3.43 - # Reduce usage of the referenced objects.
3.44 -
3.45 - if depends:
3.46 - for origin in depends:
3.47 - usage[origin].remove(path)
3.48 -
3.49 - del usage[path]
3.50 - have_next = True
3.51 -
3.52 - if not have_next:
3.53 - raise ProgramError("Modules with unresolvable dependencies exist: %s" % ", ".join(usage.keys()))
3.54 + try:
3.55 + ordered = order_dependencies(self.depends)
3.56 + except ValueError, exc:
3.57 + raise ProgramError("Modules with unresolvable dependencies exist: %s" % ", ".join(exc.args[0].keys()))
3.58
3.59 if "__main__" in ordered:
3.60 ordered.remove("__main__")
4.1 --- a/inspector.py Sat Mar 11 01:04:34 2017 +0100
4.2 +++ b/inspector.py Mon Mar 13 17:53:19 2017 +0100
4.3 @@ -766,7 +766,7 @@
4.4 if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):
4.5 return InstanceRef(name_ref.reference().instance_of())
4.6
4.7 - elif isinstance(name_ref, NameRef):
4.8 + elif isinstance(name_ref, (NameRef, AccessRef)):
4.9 return InvocationRef(name_ref)
4.10
4.11 # Provide a general reference to indicate that something is produced
4.12 @@ -902,7 +902,8 @@
4.13 branches = self.trackers[-1].tracking_name(name)
4.14 if branches:
4.15 self.record_branches_for_access(branches, name, None)
4.16 - return self.record_access_details(name, None, None, None)
4.17 + return self.record_access_details(name, None, self.in_assignment,
4.18 + self.in_invocation)
4.19 return None
4.20
4.21 def process_operator_chain(self, nodes, fn):
5.1 --- a/resolving.py Sat Mar 11 01:04:34 2017 +0100
5.2 +++ b/resolving.py Mon Mar 13 17:53:19 2017 +0100
5.3 @@ -283,13 +283,12 @@
5.4 # but they may be resolvable later.
5.5
5.6 if not ref:
5.7 - if not invocation:
5.8
5.9 - # Record the path used for tracking purposes
5.10 - # alongside original name, attribute and access
5.11 - # number details.
5.12 + # Record the path used for tracking purposes
5.13 + # alongside original name, attribute and access
5.14 + # number details.
5.15
5.16 - aliased_names[i] = path, name_ref.original_name, name_ref.attrnames, name_ref.number
5.17 + aliased_names[i] = path, name_ref.original_name, name_ref.attrnames, name_ref.number
5.18
5.19 continue
5.20
5.21 @@ -297,33 +296,30 @@
5.22
5.23 elif isinstance(name_ref, LocalNameRef):
5.24 key = "%s.%s" % (path, name_ref.name)
5.25 - origin = self.name_references.get(key)
5.26 + ref = self.name_references.get(key)
5.27
5.28 # Accesses that do not refer to known static objects
5.29 # cannot be resolved, but they may be resolvable later.
5.30
5.31 - if not origin:
5.32 - if not invocation:
5.33 + if not ref:
5.34
5.35 - # Record the path used for tracking purposes
5.36 - # alongside original name, attribute and access
5.37 - # number details.
5.38 + # Record the path used for tracking purposes
5.39 + # alongside original name, attribute and access
5.40 + # number details.
5.41
5.42 - aliased_names[i] = path, name_ref.name, None, name_ref.number
5.43 + aliased_names[i] = path, name_ref.name, None, name_ref.number
5.44
5.45 continue
5.46
5.47 - ref = self.get_resolved_object(origin)
5.48 + ref = self.get_resolved_object(ref.get_origin())
5.49 if not ref:
5.50 continue
5.51
5.52 elif isinstance(name_ref, NameRef):
5.53 key = "%s.%s" % (path, name_ref.name)
5.54 - origin = self.name_references.get(key)
5.55 - if not origin:
5.56 - continue
5.57 + ref = self.name_references.get(key)
5.58
5.59 - ref = self.get_resolved_object(origin)
5.60 + ref = ref and self.get_resolved_object(ref.get_origin())
5.61 if not ref:
5.62 continue
5.63
5.64 @@ -338,7 +334,7 @@
5.65
5.66 # Convert class invocations to instances.
5.67
5.68 - if ref and invocation:
5.69 + if ref and invocation or ref.has_kind("<invoke>"):
5.70 ref = self.convert_invocation(ref)
5.71
5.72 if ref and not ref.has_kind("<var>"):
6.1 --- a/templates/ops.c Sat Mar 11 01:04:34 2017 +0100
6.2 +++ b/templates/ops.c Mon Mar 13 17:53:19 2017 +0100
6.3 @@ -34,17 +34,17 @@
6.4
6.5 __attr __load_static_ignore(__ref obj)
6.6 {
6.7 - return (__attr) {.value=obj};
6.8 + return __ATTRVALUE(obj);
6.9 }
6.10
6.11 __attr __load_static_replace(__ref context, __ref obj)
6.12 {
6.13 - return __update_context(context, (__attr) {.value=obj});
6.14 + return __update_context(context, __ATTRVALUE(obj));
6.15 }
6.16
6.17 __attr __load_static_test(__ref context, __ref obj)
6.18 {
6.19 - return __test_context(context, (__attr) {.value=obj});
6.20 + return __test_context(context, __ATTRVALUE(obj));
6.21 }
6.22
6.23 /* Direct retrieval operations, returning and setting attributes. */
6.24 @@ -281,9 +281,9 @@
6.25 {
6.26 /* Set the local context to the specified context if appropriate. */
6.27
6.28 - if (__test_context_update(context, (__attr) {.value=value}))
6.29 + if (__test_context_update(context, __ATTRVALUE(value)))
6.30 contexts[target] = context;
6.31 - return (__attr) {.value=value};
6.32 + return __ATTRVALUE(value);
6.33 }
6.34
6.35 /* Context testing for invocations. */
7.1 --- a/translator.py Sat Mar 11 01:04:34 2017 +0100
7.2 +++ b/translator.py Mon Mar 13 17:53:19 2017 +0100
7.3 @@ -869,7 +869,7 @@
7.4
7.5 # Produce an appropriate access to an attribute's value.
7.6
7.7 - name_to_value = "%s.value" % name
7.8 + name_to_value = "%s.value" % encode_path(name)
7.9
7.10 # Write a test that raises a TypeError upon failure.
7.11
7.12 @@ -1440,7 +1440,7 @@
7.13 # Find any invocation or alias details.
7.14
7.15 name = self.get_name_for_tracking(n.name, is_global=is_global)
7.16 - location = not expr and self.get_access_location(name)
7.17 + location = not expr and self.get_access_location(name) or None
7.18
7.19 # Mark any local assignments as volatile in exception blocks.
7.20