# HG changeset patch # User Paul Boddie # Date 1489092938 -3600 # Node ID cdca907836ae8aa79385f20fdf99228e618171e3 # Parent f355f9f6647cd5dfc825f70ba38c4d7842075644# Parent 300df9719aec05e1bb901b46aec95721bbd1e17e Merged changes from the default branch. diff -r f355f9f6647c -r cdca907836ae deducer.py --- a/deducer.py Wed Mar 08 14:54:06 2017 +0100 +++ b/deducer.py Thu Mar 09 21:55:38 2017 +0100 @@ -1680,10 +1680,21 @@ access_location = self.const_accesses[access_location] location, name, attrnames, access_number = access_location + attrnames = attrnames and attrnames.split(".") + remaining = attrnames and len(attrnames) > 1 + + # Alias has remaining attributes: reference details do not + # correspond to the accessor; the remaining attributes would + # need to be traversed first. + + if remaining: + return # Alias references an attribute access. - if attrnames: + attrname = attrnames and attrnames[0] + + if attrname: attrs = [] for attrtype, object_type, attr in self.referenced_attrs[access_location]: attrs.append(attr) diff -r f355f9f6647c -r cdca907836ae inspector.py --- a/inspector.py Wed Mar 08 14:54:06 2017 +0100 +++ b/inspector.py Thu Mar 09 21:55:38 2017 +0100 @@ -863,34 +863,48 @@ ref = self.find_name(n.name) if ref: + self.record_name_access(n.name, True) return ResolvedNameRef(n.name, ref, is_global=True) # Explicitly-declared global names. elif self.in_function and n.name in self.scope_globals[path]: + self.record_name_access(n.name, True) return NameRef(n.name, is_global=True) # Examine other names. else: - tracker = self.trackers[-1] # Check local names. - branches = tracker.tracking_name(n.name) + access_number = self.record_name_access(n.name) # Local name. - if branches: - self.record_branches_for_access(branches, n.name, None) - access_number = self.record_access_details(n.name, None, None, None) + if access_number is not None: return LocalNameRef(n.name, access_number) # Possible global or built-in name. else: + self.record_name_access(n.name, True) return NameRef(n.name, is_global=True) + def record_name_access(self, name, is_global=False): + + """ + Record an access involving 'name' if the name is being tracked, using + 'is_global' to indicate whether the name is global. + """ + + name = self.get_name_for_tracking(name, is_global=is_global) + branches = self.trackers[-1].tracking_name(name) + if branches: + self.record_branches_for_access(branches, name, None) + return self.record_access_details(name, None, None, None) + return None + def process_operator_chain(self, nodes, fn): """ diff -r f355f9f6647c -r cdca907836ae results.py --- a/results.py Wed Mar 08 14:54:06 2017 +0100 +++ b/results.py Thu Mar 09 21:55:38 2017 +0100 @@ -42,6 +42,9 @@ def access_location(self): return None + def access_locations(self): + return None + def context_identity(self): return None diff -r f355f9f6647c -r cdca907836ae translator.py --- a/translator.py Wed Mar 08 14:54:06 2017 +0100 +++ b/translator.py Thu Mar 09 21:55:38 2017 +0100 @@ -33,7 +33,7 @@ from results import Result from transresults import TrConstantValueRef, TrInstanceRef, \ TrLiteralSequenceRef, TrResolvedNameRef, \ - AttrResult, Expression, InstantiationResult, \ + AliasResult, AttrResult, Expression, InstantiationResult, \ InvocationResult, LogicalOperationResult, \ LogicalResult, NegationResult, PredefinedConstantRef, \ ReturnRef @@ -631,8 +631,10 @@ access_location = self.deducer.const_accesses.get(location) refs = [] - for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]: - refs.append(attr) + l = self.deducer.referenced_attrs.get(access_location or location) + if l: + for attrtype, objpath, attr in l: + refs.append(attr) return refs def get_referenced_attribute_invocations(self, location): @@ -645,11 +647,16 @@ access_location = self.deducer.const_accesses.get(location) return self.deducer.reference_invocations_unsuitable.get(access_location or location) - def get_accessor_kinds(self, location): - - "Return the accessor kinds for 'location'." - - return self.deducer.accessor_kinds.get(location) + def get_accessor_kinds(self, locations): + + "Return the accessor kinds for 'locations'." + + accessor_kinds = set() + for location in locations: + kinds = self.deducer.accessor_kinds.get(location) + if kinds: + accessor_kinds.update(kinds) + return accessor_kinds def get_access_location(self, name, attrnames=None): @@ -1019,6 +1026,7 @@ objpath = expr.get_origin() location = expr.access_location() + locations = expr.access_locations() # Identified target details. @@ -1072,7 +1080,10 @@ # Test for functions and methods. context_required = self.is_method(objpath) - accessor_kinds = self.get_accessor_kinds(location) + + accessor_kinds = location and self.get_accessor_kinds([location]) or \ + locations and self.get_accessor_kinds(locations) + instance_accessor = accessor_kinds and \ len(accessor_kinds) == 1 and \ first(accessor_kinds) == "" @@ -1106,8 +1117,12 @@ # Determine any readily-accessible target identity. - target_identity = target or expr.is_name() and str(expr) or None - target_var = target_identity or "__tmp_targets[%d]" % self.function_target + target_named = expr.is_name() and str(expr) or None + target_stored = "__tmp_targets[%d]" % self.function_target + + target_identity = target or target_named + target_var = target_identity or target_stored + context_var = target_named or target_stored if not target_identity: self.record_temp("__tmp_targets") @@ -1123,7 +1138,7 @@ if have_access_context: args = ["__ATTRVALUE(%s)" % context_identity] else: - args = ["__CONTEXT_AS_VALUE(%s)" % target_var] + args = ["__CONTEXT_AS_VALUE(%s)" % context_var] else: args = ["__NULL"] @@ -1229,18 +1244,26 @@ # Without a known specific callable, the expression provides the target. if not target or context_required: - if target: + + # The context is set in the expression. + + if target and not target_named: + + # Test whether the expression provides anything. + if expr: stages.append(str(expr)) + elif not target_identity: stages.append("%s = %s" % (target_var, expr)) - # Any specific callable is then obtained. + # Any specific callable is then obtained for invocation. if target: stages.append(target) - # Methods accessed via unidentified accessors are obtained. + # Methods accessed via unidentified accessors are obtained for + # invocation. elif function: if context_required: @@ -1249,7 +1272,7 @@ context_identity, target_var)) else: stages.append("__get_function(__CONTEXT_AS_VALUE(%s).value, %s)" % ( - target_var, target_var)) + context_var, target_var)) else: stages.append("__load_via_object(%s.value, __fn__).fn" % target_var) @@ -1414,10 +1437,10 @@ parameter = n.name == "self" and self.in_method() or \ parameters and n.name in parameters - # Find any invocation details. + # Find any invocation or alias details. name = self.get_name_for_tracking(n.name, is_global=is_global) - location = self.get_access_location(name) + location = not expr and self.get_access_location(name) # Mark any local assignments as volatile in exception blocks. @@ -1428,7 +1451,32 @@ # static namespace members. The reference should be configured to return # such names. - return TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, location=location) + name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, + location=location) + result = self.get_aliases(name_ref) + return result or name_ref + + def get_aliases(self, name_ref): + + "Return alias references for the given 'name_ref'." + + location = name_ref.access_location() + + accessor_locations = location and self.deducer.get_accessors_for_access(location) + alias_refs = set() + access_locations = set() + + if accessor_locations: + for accessor_location in accessor_locations: + aliased_accesses = self.deducer.alias_index.get(accessor_location) + if not aliased_accesses: + continue + access_locations.update(aliased_accesses) + refs = self.deducer.referenced_objects.get(accessor_location) + if refs: + alias_refs.update(refs) + + return AliasResult(name_ref, alias_refs, access_locations) def make_volatile(self, name): diff -r f355f9f6647c -r cdca907836ae transresults.py --- a/transresults.py Wed Mar 08 14:54:06 2017 +0100 +++ b/transresults.py Thu Mar 09 21:55:38 2017 +0100 @@ -21,7 +21,7 @@ from common import first, InstructionSequence from encoders import encode_instructions, encode_literal_constant, encode_path -from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, \ +from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \ ResolvedNameRef, Result # Classes representing intermediate translation results. @@ -70,6 +70,9 @@ def access_location(self): return self.location + def access_locations(self): + return self.location and [self.location] + def __str__(self): "Return an output representation of the referenced name." @@ -94,7 +97,7 @@ # Eliminate assignments between constants. - if ref and isinstance(self.expr, ResolvedNameRef) and self.expr.static(): + if ref and self.expr.static(): return "" # Qualified names must be converted into parent-relative assignments. @@ -176,6 +179,9 @@ def access_location(self): return self.location + def access_locations(self): + return self.location and [self.location] + def context(self): return self.context_identity @@ -199,6 +205,62 @@ def __repr__(self): return "AttrResult(%r, %r, %r)" % (self.instructions, self.refs, self.location) +class AliasResult(NameRef, Result): + + "An alias for other values." + + def __init__(self, name_ref, refs, locations): + NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) + self.name_ref = name_ref + self.refs = refs + self.locations = locations + + def references(self): + ref = self.name_ref.reference() + return self.refs or ref and [ref] or None + + def reference(self): + refs = self.references() + return len(refs) == 1 and first(refs) or None + + def access_location(self): + return len(self.locations) == 1 and first(self.locations) or None + + def access_locations(self): + return self.locations + + def get_name(self): + ref = self.reference() + return ref and ref.get_name() + + def get_origin(self): + ref = self.reference() + return ref and ref.get_origin() + + def static(self): + ref = self.reference() + return ref and ref.static() + + def final(self): + ref = self.reference() + return ref and ref.final() + + def has_kind(self, kinds): + if not self.refs: + return self.name_ref.has_kind(kinds) + + for ref in self.refs: + if ref.has_kind(kinds): + return True + + return False + + def __str__(self): + return str(self.name_ref) + + def __repr__(self): + return "AliasResult(%r, %r)" % (self.name_ref, self.refs) + class InvocationResult(Result, InstructionSequence): "A translation result for an invocation."