1.1 --- a/deducer.py Wed Mar 08 14:54:06 2017 +0100
1.2 +++ b/deducer.py Thu Mar 09 21:55:38 2017 +0100
1.3 @@ -1680,10 +1680,21 @@
1.4 access_location = self.const_accesses[access_location]
1.5
1.6 location, name, attrnames, access_number = access_location
1.7 + attrnames = attrnames and attrnames.split(".")
1.8 + remaining = attrnames and len(attrnames) > 1
1.9 +
1.10 + # Alias has remaining attributes: reference details do not
1.11 + # correspond to the accessor; the remaining attributes would
1.12 + # need to be traversed first.
1.13 +
1.14 + if remaining:
1.15 + return
1.16
1.17 # Alias references an attribute access.
1.18
1.19 - if attrnames:
1.20 + attrname = attrnames and attrnames[0]
1.21 +
1.22 + if attrname:
1.23 attrs = []
1.24 for attrtype, object_type, attr in self.referenced_attrs[access_location]:
1.25 attrs.append(attr)
2.1 --- a/inspector.py Wed Mar 08 14:54:06 2017 +0100
2.2 +++ b/inspector.py Thu Mar 09 21:55:38 2017 +0100
2.3 @@ -863,34 +863,48 @@
2.4
2.5 ref = self.find_name(n.name)
2.6 if ref:
2.7 + self.record_name_access(n.name, True)
2.8 return ResolvedNameRef(n.name, ref, is_global=True)
2.9
2.10 # Explicitly-declared global names.
2.11
2.12 elif self.in_function and n.name in self.scope_globals[path]:
2.13 + self.record_name_access(n.name, True)
2.14 return NameRef(n.name, is_global=True)
2.15
2.16 # Examine other names.
2.17
2.18 else:
2.19 - tracker = self.trackers[-1]
2.20
2.21 # Check local names.
2.22
2.23 - branches = tracker.tracking_name(n.name)
2.24 + access_number = self.record_name_access(n.name)
2.25
2.26 # Local name.
2.27
2.28 - if branches:
2.29 - self.record_branches_for_access(branches, n.name, None)
2.30 - access_number = self.record_access_details(n.name, None, None, None)
2.31 + if access_number is not None:
2.32 return LocalNameRef(n.name, access_number)
2.33
2.34 # Possible global or built-in name.
2.35
2.36 else:
2.37 + self.record_name_access(n.name, True)
2.38 return NameRef(n.name, is_global=True)
2.39
2.40 + def record_name_access(self, name, is_global=False):
2.41 +
2.42 + """
2.43 + Record an access involving 'name' if the name is being tracked, using
2.44 + 'is_global' to indicate whether the name is global.
2.45 + """
2.46 +
2.47 + name = self.get_name_for_tracking(name, is_global=is_global)
2.48 + branches = self.trackers[-1].tracking_name(name)
2.49 + if branches:
2.50 + self.record_branches_for_access(branches, name, None)
2.51 + return self.record_access_details(name, None, None, None)
2.52 + return None
2.53 +
2.54 def process_operator_chain(self, nodes, fn):
2.55
2.56 """
3.1 --- a/results.py Wed Mar 08 14:54:06 2017 +0100
3.2 +++ b/results.py Thu Mar 09 21:55:38 2017 +0100
3.3 @@ -42,6 +42,9 @@
3.4 def access_location(self):
3.5 return None
3.6
3.7 + def access_locations(self):
3.8 + return None
3.9 +
3.10 def context_identity(self):
3.11 return None
3.12
4.1 --- a/translator.py Wed Mar 08 14:54:06 2017 +0100
4.2 +++ b/translator.py Thu Mar 09 21:55:38 2017 +0100
4.3 @@ -33,7 +33,7 @@
4.4 from results import Result
4.5 from transresults import TrConstantValueRef, TrInstanceRef, \
4.6 TrLiteralSequenceRef, TrResolvedNameRef, \
4.7 - AttrResult, Expression, InstantiationResult, \
4.8 + AliasResult, AttrResult, Expression, InstantiationResult, \
4.9 InvocationResult, LogicalOperationResult, \
4.10 LogicalResult, NegationResult, PredefinedConstantRef, \
4.11 ReturnRef
4.12 @@ -631,8 +631,10 @@
4.13
4.14 access_location = self.deducer.const_accesses.get(location)
4.15 refs = []
4.16 - for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]:
4.17 - refs.append(attr)
4.18 + l = self.deducer.referenced_attrs.get(access_location or location)
4.19 + if l:
4.20 + for attrtype, objpath, attr in l:
4.21 + refs.append(attr)
4.22 return refs
4.23
4.24 def get_referenced_attribute_invocations(self, location):
4.25 @@ -645,11 +647,16 @@
4.26 access_location = self.deducer.const_accesses.get(location)
4.27 return self.deducer.reference_invocations_unsuitable.get(access_location or location)
4.28
4.29 - def get_accessor_kinds(self, location):
4.30 -
4.31 - "Return the accessor kinds for 'location'."
4.32 -
4.33 - return self.deducer.accessor_kinds.get(location)
4.34 + def get_accessor_kinds(self, locations):
4.35 +
4.36 + "Return the accessor kinds for 'locations'."
4.37 +
4.38 + accessor_kinds = set()
4.39 + for location in locations:
4.40 + kinds = self.deducer.accessor_kinds.get(location)
4.41 + if kinds:
4.42 + accessor_kinds.update(kinds)
4.43 + return accessor_kinds
4.44
4.45 def get_access_location(self, name, attrnames=None):
4.46
4.47 @@ -1019,6 +1026,7 @@
4.48
4.49 objpath = expr.get_origin()
4.50 location = expr.access_location()
4.51 + locations = expr.access_locations()
4.52
4.53 # Identified target details.
4.54
4.55 @@ -1072,7 +1080,10 @@
4.56 # Test for functions and methods.
4.57
4.58 context_required = self.is_method(objpath)
4.59 - accessor_kinds = self.get_accessor_kinds(location)
4.60 +
4.61 + accessor_kinds = location and self.get_accessor_kinds([location]) or \
4.62 + locations and self.get_accessor_kinds(locations)
4.63 +
4.64 instance_accessor = accessor_kinds and \
4.65 len(accessor_kinds) == 1 and \
4.66 first(accessor_kinds) == "<instance>"
4.67 @@ -1106,8 +1117,12 @@
4.68
4.69 # Determine any readily-accessible target identity.
4.70
4.71 - target_identity = target or expr.is_name() and str(expr) or None
4.72 - target_var = target_identity or "__tmp_targets[%d]" % self.function_target
4.73 + target_named = expr.is_name() and str(expr) or None
4.74 + target_stored = "__tmp_targets[%d]" % self.function_target
4.75 +
4.76 + target_identity = target or target_named
4.77 + target_var = target_identity or target_stored
4.78 + context_var = target_named or target_stored
4.79
4.80 if not target_identity:
4.81 self.record_temp("__tmp_targets")
4.82 @@ -1123,7 +1138,7 @@
4.83 if have_access_context:
4.84 args = ["__ATTRVALUE(%s)" % context_identity]
4.85 else:
4.86 - args = ["__CONTEXT_AS_VALUE(%s)" % target_var]
4.87 + args = ["__CONTEXT_AS_VALUE(%s)" % context_var]
4.88 else:
4.89 args = ["__NULL"]
4.90
4.91 @@ -1229,18 +1244,26 @@
4.92 # Without a known specific callable, the expression provides the target.
4.93
4.94 if not target or context_required:
4.95 - if target:
4.96 +
4.97 + # The context is set in the expression.
4.98 +
4.99 + if target and not target_named:
4.100 +
4.101 + # Test whether the expression provides anything.
4.102 +
4.103 if expr:
4.104 stages.append(str(expr))
4.105 +
4.106 elif not target_identity:
4.107 stages.append("%s = %s" % (target_var, expr))
4.108
4.109 - # Any specific callable is then obtained.
4.110 + # Any specific callable is then obtained for invocation.
4.111
4.112 if target:
4.113 stages.append(target)
4.114
4.115 - # Methods accessed via unidentified accessors are obtained.
4.116 + # Methods accessed via unidentified accessors are obtained for
4.117 + # invocation.
4.118
4.119 elif function:
4.120 if context_required:
4.121 @@ -1249,7 +1272,7 @@
4.122 context_identity, target_var))
4.123 else:
4.124 stages.append("__get_function(__CONTEXT_AS_VALUE(%s).value, %s)" % (
4.125 - target_var, target_var))
4.126 + context_var, target_var))
4.127 else:
4.128 stages.append("__load_via_object(%s.value, __fn__).fn" % target_var)
4.129
4.130 @@ -1414,10 +1437,10 @@
4.131 parameter = n.name == "self" and self.in_method() or \
4.132 parameters and n.name in parameters
4.133
4.134 - # Find any invocation details.
4.135 + # Find any invocation or alias details.
4.136
4.137 name = self.get_name_for_tracking(n.name, is_global=is_global)
4.138 - location = self.get_access_location(name)
4.139 + location = not expr and self.get_access_location(name)
4.140
4.141 # Mark any local assignments as volatile in exception blocks.
4.142
4.143 @@ -1428,7 +1451,32 @@
4.144 # static namespace members. The reference should be configured to return
4.145 # such names.
4.146
4.147 - return TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, location=location)
4.148 + name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global,
4.149 + location=location)
4.150 + result = self.get_aliases(name_ref)
4.151 + return result or name_ref
4.152 +
4.153 + def get_aliases(self, name_ref):
4.154 +
4.155 + "Return alias references for the given 'name_ref'."
4.156 +
4.157 + location = name_ref.access_location()
4.158 +
4.159 + accessor_locations = location and self.deducer.get_accessors_for_access(location)
4.160 + alias_refs = set()
4.161 + access_locations = set()
4.162 +
4.163 + if accessor_locations:
4.164 + for accessor_location in accessor_locations:
4.165 + aliased_accesses = self.deducer.alias_index.get(accessor_location)
4.166 + if not aliased_accesses:
4.167 + continue
4.168 + access_locations.update(aliased_accesses)
4.169 + refs = self.deducer.referenced_objects.get(accessor_location)
4.170 + if refs:
4.171 + alias_refs.update(refs)
4.172 +
4.173 + return AliasResult(name_ref, alias_refs, access_locations)
4.174
4.175 def make_volatile(self, name):
4.176
5.1 --- a/transresults.py Wed Mar 08 14:54:06 2017 +0100
5.2 +++ b/transresults.py Thu Mar 09 21:55:38 2017 +0100
5.3 @@ -21,7 +21,7 @@
5.4
5.5 from common import first, InstructionSequence
5.6 from encoders import encode_instructions, encode_literal_constant, encode_path
5.7 -from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, \
5.8 +from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \
5.9 ResolvedNameRef, Result
5.10
5.11 # Classes representing intermediate translation results.
5.12 @@ -70,6 +70,9 @@
5.13 def access_location(self):
5.14 return self.location
5.15
5.16 + def access_locations(self):
5.17 + return self.location and [self.location]
5.18 +
5.19 def __str__(self):
5.20
5.21 "Return an output representation of the referenced name."
5.22 @@ -94,7 +97,7 @@
5.23
5.24 # Eliminate assignments between constants.
5.25
5.26 - if ref and isinstance(self.expr, ResolvedNameRef) and self.expr.static():
5.27 + if ref and self.expr.static():
5.28 return ""
5.29
5.30 # Qualified names must be converted into parent-relative assignments.
5.31 @@ -176,6 +179,9 @@
5.32 def access_location(self):
5.33 return self.location
5.34
5.35 + def access_locations(self):
5.36 + return self.location and [self.location]
5.37 +
5.38 def context(self):
5.39 return self.context_identity
5.40
5.41 @@ -199,6 +205,62 @@
5.42 def __repr__(self):
5.43 return "AttrResult(%r, %r, %r)" % (self.instructions, self.refs, self.location)
5.44
5.45 +class AliasResult(NameRef, Result):
5.46 +
5.47 + "An alias for other values."
5.48 +
5.49 + def __init__(self, name_ref, refs, locations):
5.50 + NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name())
5.51 + self.name_ref = name_ref
5.52 + self.refs = refs
5.53 + self.locations = locations
5.54 +
5.55 + def references(self):
5.56 + ref = self.name_ref.reference()
5.57 + return self.refs or ref and [ref] or None
5.58 +
5.59 + def reference(self):
5.60 + refs = self.references()
5.61 + return len(refs) == 1 and first(refs) or None
5.62 +
5.63 + def access_location(self):
5.64 + return len(self.locations) == 1 and first(self.locations) or None
5.65 +
5.66 + def access_locations(self):
5.67 + return self.locations
5.68 +
5.69 + def get_name(self):
5.70 + ref = self.reference()
5.71 + return ref and ref.get_name()
5.72 +
5.73 + def get_origin(self):
5.74 + ref = self.reference()
5.75 + return ref and ref.get_origin()
5.76 +
5.77 + def static(self):
5.78 + ref = self.reference()
5.79 + return ref and ref.static()
5.80 +
5.81 + def final(self):
5.82 + ref = self.reference()
5.83 + return ref and ref.final()
5.84 +
5.85 + def has_kind(self, kinds):
5.86 + if not self.refs:
5.87 + return self.name_ref.has_kind(kinds)
5.88 +
5.89 + for ref in self.refs:
5.90 + if ref.has_kind(kinds):
5.91 + return True
5.92 +
5.93 + return False
5.94 +
5.95 + def __str__(self):
5.96 + return str(self.name_ref)
5.97 +
5.98 + def __repr__(self):
5.99 + return "AliasResult(%r, %r)" % (self.name_ref, self.refs)
5.100 +
5.101 class InvocationResult(Result, InstructionSequence):
5.102
5.103 "A translation result for an invocation."