1.1 --- a/deducer.py Thu Sep 29 19:25:48 2016 +0200
1.2 +++ b/deducer.py Thu Sep 29 23:11:26 2016 +0200
1.3 @@ -25,6 +25,7 @@
1.4 from encoders import encode_attrnames, encode_access_location, \
1.5 encode_constrained, encode_location, encode_usage, \
1.6 get_kinds, test_for_kinds, test_for_type
1.7 +from errors import DeduceError
1.8 from os.path import join
1.9 from referencing import combine_types, is_single_class_type, separate_types, \
1.10 Reference
1.11 @@ -81,6 +82,10 @@
1.12
1.13 self.modified_attributes = {}
1.14
1.15 + # Accesses that are assignments.
1.16 +
1.17 + self.reference_assignments = set()
1.18 +
1.19 # Map locations to types, constrained indicators and attributes.
1.20
1.21 self.accessor_class_types = {}
1.22 @@ -520,97 +525,98 @@
1.23
1.24 referenced_attrs = self.referenced_attrs[location]
1.25
1.26 - if referenced_attrs:
1.27 -
1.28 - # Record attribute information for each name used on the
1.29 - # accessor.
1.30 -
1.31 - attrname = get_attrname_from_location(location)
1.32 -
1.33 - all_accessed_attrs = set()
1.34 - all_providers = set()
1.35 -
1.36 - # Obtain provider and attribute details for this kind of
1.37 - # object.
1.38 -
1.39 - for attrtype, object_type, attr in referenced_attrs:
1.40 - all_accessed_attrs.add(attr)
1.41 - all_providers.add(object_type)
1.42 -
1.43 - all_general_providers = self.get_most_general_types(all_providers)
1.44 -
1.45 - # Determine which attributes would be provided by the
1.46 - # accessor types upheld by a guard.
1.47 -
1.48 - if guarded:
1.49 - guard_attrs = set()
1.50 - for _attrtype, object_type, attr in \
1.51 - self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types):
1.52 - guard_attrs.add(attr)
1.53 + if not referenced_attrs:
1.54 + raise DeduceError, location
1.55 +
1.56 + # Record attribute information for each name used on the
1.57 + # accessor.
1.58 +
1.59 + attrname = get_attrname_from_location(location)
1.60 +
1.61 + all_accessed_attrs = set()
1.62 + all_providers = set()
1.63 +
1.64 + # Obtain provider and attribute details for this kind of
1.65 + # object.
1.66 +
1.67 + for attrtype, object_type, attr in referenced_attrs:
1.68 + all_accessed_attrs.add(attr)
1.69 + all_providers.add(object_type)
1.70 +
1.71 + all_general_providers = self.get_most_general_types(all_providers)
1.72 +
1.73 + # Determine which attributes would be provided by the
1.74 + # accessor types upheld by a guard.
1.75 +
1.76 + if guarded:
1.77 + guard_attrs = set()
1.78 + for _attrtype, object_type, attr in \
1.79 + self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types):
1.80 + guard_attrs.add(attr)
1.81 + else:
1.82 + guard_attrs = None
1.83 +
1.84 + self.reference_all_attrs[location] = all_accessed_attrs
1.85 +
1.86 + # Constrained accesses guarantee the nature of the accessor.
1.87 + # However, there may still be many types involved.
1.88 +
1.89 + if constrained:
1.90 + if single_accessor_type:
1.91 + self.reference_test_types[location] = test_for_type("constrained-specific", first(all_accessor_types))
1.92 + elif single_accessor_class_type:
1.93 + self.reference_test_types[location] = "constrained-specific-object"
1.94 + elif single_accessor_general_type:
1.95 + self.reference_test_types[location] = test_for_type("constrained-common", first(all_accessor_general_types))
1.96 + elif single_accessor_general_class_type:
1.97 + self.reference_test_types[location] = "constrained-common-object"
1.98 else:
1.99 - guard_attrs = None
1.100 -
1.101 - self.reference_all_attrs[location] = all_accessed_attrs
1.102 -
1.103 - # Constrained accesses guarantee the nature of the accessor.
1.104 - # However, there may still be many types involved.
1.105 -
1.106 - if constrained:
1.107 - if single_accessor_type:
1.108 - self.reference_test_types[location] = test_for_type("constrained-specific", first(all_accessor_types))
1.109 - elif single_accessor_class_type:
1.110 - self.reference_test_types[location] = "constrained-specific-object"
1.111 - elif single_accessor_general_type:
1.112 - self.reference_test_types[location] = test_for_type("constrained-common", first(all_accessor_general_types))
1.113 - elif single_accessor_general_class_type:
1.114 - self.reference_test_types[location] = "constrained-common-object"
1.115 + self.reference_test_types[location] = "constrained-many"
1.116 +
1.117 + # Suitably guarded accesses, where the nature of the
1.118 + # accessor can be guaranteed, do not require the attribute
1.119 + # involved to be validated. Otherwise, for unguarded
1.120 + # accesses, access-level tests are required.
1.121 +
1.122 + elif guarded and all_accessed_attrs.issubset(guard_attrs):
1.123 + if single_accessor_type:
1.124 + self.reference_test_types[location] = test_for_type("guarded-specific", first(all_accessor_types))
1.125 + elif single_accessor_class_type:
1.126 + self.reference_test_types[location] = "guarded-specific-object"
1.127 + elif single_accessor_general_type:
1.128 + self.reference_test_types[location] = test_for_type("guarded-common", first(all_accessor_general_types))
1.129 + elif single_accessor_general_class_type:
1.130 + self.reference_test_types[location] = "guarded-common-object"
1.131 +
1.132 + # Record the need to test the type of anonymous and
1.133 + # unconstrained accessors.
1.134 +
1.135 + elif len(all_providers) == 1:
1.136 + provider = first(all_providers)
1.137 + if provider != '__builtins__.object':
1.138 + all_accessor_kinds = set(get_kinds(all_accessor_types))
1.139 + if len(all_accessor_kinds) == 1:
1.140 + test_type = test_for_kinds("specific", all_accessor_kinds)
1.141 else:
1.142 - self.reference_test_types[location] = "constrained-many"
1.143 -
1.144 - # Suitably guarded accesses, where the nature of the
1.145 - # accessor can be guaranteed, do not require the attribute
1.146 - # involved to be validated. Otherwise, for unguarded
1.147 - # accesses, access-level tests are required.
1.148 -
1.149 - elif guarded and all_accessed_attrs.issubset(guard_attrs):
1.150 - if single_accessor_type:
1.151 - self.reference_test_types[location] = test_for_type("guarded-specific", first(all_accessor_types))
1.152 - elif single_accessor_class_type:
1.153 - self.reference_test_types[location] = "guarded-specific-object"
1.154 - elif single_accessor_general_type:
1.155 - self.reference_test_types[location] = test_for_type("guarded-common", first(all_accessor_general_types))
1.156 - elif single_accessor_general_class_type:
1.157 - self.reference_test_types[location] = "guarded-common-object"
1.158 -
1.159 - # Record the need to test the type of anonymous and
1.160 - # unconstrained accessors.
1.161 -
1.162 - elif len(all_providers) == 1:
1.163 - provider = first(all_providers)
1.164 - if provider != '__builtins__.object':
1.165 - all_accessor_kinds = set(get_kinds(all_accessor_types))
1.166 - if len(all_accessor_kinds) == 1:
1.167 - test_type = test_for_kinds("specific", all_accessor_kinds)
1.168 - else:
1.169 - test_type = "specific-object"
1.170 - self.reference_test_types[location] = test_type
1.171 - self.reference_test_accessor_types[location] = provider
1.172 -
1.173 - elif len(all_general_providers) == 1:
1.174 - provider = first(all_general_providers)
1.175 - if provider != '__builtins__.object':
1.176 - all_accessor_kinds = set(get_kinds(all_accessor_general_types))
1.177 - if len(all_accessor_kinds) == 1:
1.178 - test_type = test_for_kinds("common", all_accessor_kinds)
1.179 - else:
1.180 - test_type = "common-object"
1.181 - self.reference_test_types[location] = test_type
1.182 - self.reference_test_accessor_types[location] = provider
1.183 -
1.184 - # Record the need to test the identity of the attribute.
1.185 -
1.186 - else:
1.187 - self.reference_test_types[location] = "validate"
1.188 + test_type = "specific-object"
1.189 + self.reference_test_types[location] = test_type
1.190 + self.reference_test_accessor_types[location] = provider
1.191 +
1.192 + elif len(all_general_providers) == 1:
1.193 + provider = first(all_general_providers)
1.194 + if provider != '__builtins__.object':
1.195 + all_accessor_kinds = set(get_kinds(all_accessor_general_types))
1.196 + if len(all_accessor_kinds) == 1:
1.197 + test_type = test_for_kinds("common", all_accessor_kinds)
1.198 + else:
1.199 + test_type = "common-object"
1.200 + self.reference_test_types[location] = test_type
1.201 + self.reference_test_accessor_types[location] = provider
1.202 +
1.203 + # Record the need to test the identity of the attribute.
1.204 +
1.205 + else:
1.206 + self.reference_test_types[location] = "validate"
1.207
1.208 def initialise_access_plans(self):
1.209
1.210 @@ -784,6 +790,9 @@
1.211 else:
1.212 access_location = (path, None, attrnames, 0)
1.213
1.214 + if assignment:
1.215 + self.reference_assignments.add(access_location)
1.216 +
1.217 # Associate assignments with usage.
1.218
1.219 accessor_locations = self.get_accessors_for_access(access_location)
1.220 @@ -1838,16 +1847,16 @@
1.221
1.222 # Determine the method of access.
1.223
1.224 + # Identified attribute that must be accessed via its parent.
1.225 +
1.226 + if attr and attr.get_name() and (not attr.static() or location in self.reference_assignments):
1.227 + method = "direct"; origin = attr.get_name()
1.228 +
1.229 # Static, identified attribute.
1.230
1.231 - if attr and attr.static():
1.232 + elif attr and attr.static():
1.233 method = "static"; origin = attr.final()
1.234
1.235 - # Identified attribute that must be accessed via its parent.
1.236 -
1.237 - elif attr and attr.get_name():
1.238 - method = "direct"; origin = attr.get_name()
1.239 -
1.240 # Attribute accessed at a known position via its parent.
1.241
1.242 elif base or dynamic_base:
3.1 --- a/tests/chain.py Thu Sep 29 19:25:48 2016 +0200
3.2 +++ b/tests/chain.py Thu Sep 29 23:11:26 2016 +0200
3.3 @@ -9,6 +9,7 @@
3.4 n = 123
3.5 o = "123"
3.6 p = "456"
3.7 + q = 789
3.8
3.9 def main():
3.10 c = C
3.11 @@ -18,6 +19,7 @@
3.12 g = C.D.E.n
3.13 h = C.D.p
3.14 i = C.D.p.__len__
3.15 + C.D.q = 987
3.16 inst = e()
3.17 method = inst.m
3.18 return method("5")
3.19 @@ -30,6 +32,7 @@
3.20 g = C.D.E.n
3.21 h = C.D.p
3.22 i = C.D.p.__len__
3.23 +C.D.q = 987
3.24 inst = e()
3.25 method = inst.m
3.26 result2 = method("5")