1.1 --- a/deducer.py Thu Sep 29 15:37:09 2016 +0200
1.2 +++ b/deducer.py Thu Sep 29 19:25:48 2016 +0200
1.3 @@ -495,13 +495,18 @@
1.4 all_accessor_types.update(self.accessor_all_types.get(accessor_location))
1.5 all_accessor_general_types.update(self.accessor_all_general_types.get(accessor_location))
1.6
1.7 + # Obtain basic properties of the types involved in the access.
1.8 +
1.9 + single_accessor_type = len(all_accessor_types) == 1
1.10 + single_accessor_class_type = is_single_class_type(all_accessor_types)
1.11 + single_accessor_general_type = len(all_accessor_general_types) == 1
1.12 + single_accessor_general_class_type = is_single_class_type(all_accessor_general_types)
1.13 +
1.14 # Determine whether the attribute access is guarded or not.
1.15
1.16 guarded = (
1.17 - len(all_accessor_types) == 1 or
1.18 - is_single_class_type(all_accessor_types) or
1.19 - len(all_accessor_general_types) == 1 or
1.20 - is_single_class_type(all_accessor_general_types)
1.21 + single_accessor_type or single_accessor_class_type or
1.22 + single_accessor_general_type or single_accessor_general_class_type
1.23 )
1.24
1.25 if guarded:
1.26 @@ -523,7 +528,6 @@
1.27 attrname = get_attrname_from_location(location)
1.28
1.29 all_accessed_attrs = set()
1.30 - all_accessed_attrtypes = set()
1.31 all_providers = set()
1.32
1.33 # Obtain provider and attribute details for this kind of
1.34 @@ -531,7 +535,6 @@
1.35
1.36 for attrtype, object_type, attr in referenced_attrs:
1.37 all_accessed_attrs.add(attr)
1.38 - all_accessed_attrtypes.add(attrtype)
1.39 all_providers.add(object_type)
1.40
1.41 all_general_providers = self.get_most_general_types(all_providers)
1.42 @@ -540,65 +543,74 @@
1.43 # accessor types upheld by a guard.
1.44
1.45 if guarded:
1.46 - guard_attrs = [attr for _attrtype, object_type, attr in
1.47 - self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types)]
1.48 + guard_attrs = set()
1.49 + for _attrtype, object_type, attr in \
1.50 + self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types):
1.51 + guard_attrs.add(attr)
1.52 else:
1.53 guard_attrs = None
1.54
1.55 self.reference_all_attrs[location] = all_accessed_attrs
1.56 - self.reference_all_attrtypes[location] = all_accessed_attrtypes
1.57 +
1.58 + # Constrained accesses guarantee the nature of the accessor.
1.59 + # However, there may still be many types involved.
1.60 +
1.61 + if constrained:
1.62 + if single_accessor_type:
1.63 + self.reference_test_types[location] = test_for_type("constrained-specific", first(all_accessor_types))
1.64 + elif single_accessor_class_type:
1.65 + self.reference_test_types[location] = "constrained-specific-object"
1.66 + elif single_accessor_general_type:
1.67 + self.reference_test_types[location] = test_for_type("constrained-common", first(all_accessor_general_types))
1.68 + elif single_accessor_general_class_type:
1.69 + self.reference_test_types[location] = "constrained-common-object"
1.70 + else:
1.71 + self.reference_test_types[location] = "constrained-many"
1.72
1.73 # Suitably guarded accesses, where the nature of the
1.74 # accessor can be guaranteed, do not require the attribute
1.75 # involved to be validated. Otherwise, for unguarded
1.76 # accesses, access-level tests are required.
1.77
1.78 - if not constrained:
1.79 -
1.80 - # Provide informational test types.
1.81 -
1.82 - if guarded and all_accessed_attrs.issubset(guard_attrs):
1.83 - if len(all_accessor_types) == 1:
1.84 - self.reference_test_types[location] = test_for_type("guarded-specific", first(all_accessor_types))
1.85 - elif is_single_class_type(all_accessor_types):
1.86 - self.reference_test_types[location] = "guarded-specific-object"
1.87 - elif len(all_accessor_general_types) == 1:
1.88 - self.reference_test_types[location] = test_for_type("guarded-common", first(all_accessor_general_types))
1.89 - elif is_single_class_type(all_accessor_general_types):
1.90 - self.reference_test_types[location] = "guarded-common-object"
1.91 -
1.92 - # Provide active test types.
1.93 -
1.94 - else:
1.95 - # Record the need to test the type of anonymous and
1.96 - # unconstrained accessors.
1.97 -
1.98 - if len(all_providers) == 1:
1.99 - provider = list(all_providers)[0]
1.100 - if provider != '__builtins__.object':
1.101 - all_accessor_kinds = set(get_kinds(all_accessor_types))
1.102 - if len(all_accessor_kinds) == 1:
1.103 - test_type = test_for_kinds("specific", all_accessor_kinds)
1.104 - else:
1.105 - test_type = "specific-object"
1.106 - self.reference_test_types[location] = test_type
1.107 - self.reference_test_accessor_types[location] = provider
1.108 -
1.109 - elif len(all_general_providers) == 1:
1.110 - provider = list(all_general_providers)[0]
1.111 - if provider != '__builtins__.object':
1.112 - all_accessor_kinds = set(get_kinds(all_accessor_general_types))
1.113 - if len(all_accessor_kinds) == 1:
1.114 - test_type = test_for_kinds("common", all_accessor_kinds)
1.115 - else:
1.116 - test_type = "common-object"
1.117 - self.reference_test_types[location] = test_type
1.118 - self.reference_test_accessor_types[location] = provider
1.119 -
1.120 - # Record the need to test the identity of the attribute.
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] = "validate"
1.143 + test_type = "specific-object"
1.144 + self.reference_test_types[location] = test_type
1.145 + self.reference_test_accessor_types[location] = provider
1.146 +
1.147 + elif len(all_general_providers) == 1:
1.148 + provider = first(all_general_providers)
1.149 + if provider != '__builtins__.object':
1.150 + all_accessor_kinds = set(get_kinds(all_accessor_general_types))
1.151 + if len(all_accessor_kinds) == 1:
1.152 + test_type = test_for_kinds("common", all_accessor_kinds)
1.153 + else:
1.154 + test_type = "common-object"
1.155 + self.reference_test_types[location] = test_type
1.156 + self.reference_test_accessor_types[location] = provider
1.157 +
1.158 + # Record the need to test the identity of the attribute.
1.159 +
1.160 + else:
1.161 + self.reference_test_types[location] = "validate"
1.162
1.163 def initialise_access_plans(self):
1.164
1.165 @@ -1656,6 +1668,18 @@
1.166
1.167 return attrs
1.168
1.169 + constrained_specific_tests = (
1.170 + "constrained-specific-instance",
1.171 + "constrained-specific-type",
1.172 + "constrained-specific-object",
1.173 + )
1.174 +
1.175 + constrained_common_tests = (
1.176 + "constrained-common-instance",
1.177 + "constrained-common-type",
1.178 + "constrained-common-object",
1.179 + )
1.180 +
1.181 guarded_specific_tests = (
1.182 "guarded-specific-instance",
1.183 "guarded-specific-type",
1.184 @@ -1725,11 +1749,7 @@
1.185 # Determine any guard or test requirements.
1.186
1.187 constrained = location in self.access_constrained
1.188 -
1.189 - if constrained:
1.190 - test = "constrained"
1.191 - else:
1.192 - test = self.reference_test_types[location]
1.193 + test = self.reference_test_types[location]
1.194
1.195 # Determine the accessor and provider properties.
1.196
1.197 @@ -1756,40 +1776,34 @@
1.198 if ref:
1.199 base = ref.get_origin()
1.200
1.201 - # Identify single type accessors. These can be relied upon to
1.202 - # provide attributes in the same positions.
1.203 -
1.204 - elif constrained and len(accessor_types) == 1:
1.205 + # Usage of previously-generated guard and test details.
1.206 +
1.207 + elif test in self.constrained_specific_tests:
1.208 ref = first(accessor_types)
1.209
1.210 - elif constrained and len(accessor_general_types) == 1:
1.211 + elif test in self.constrained_common_tests:
1.212 ref = first(accessor_general_types)
1.213
1.214 - # Multiple type accessors may involve a single type but where both
1.215 - # the type itself (a class) or an instance of the type might be used
1.216 - # to access a class attribute.
1.217 -
1.218 - # Usage of previously-generated guard and test details.
1.219 -
1.220 elif test in self.guarded_specific_tests:
1.221 ref = first(accessor_types)
1.222
1.223 elif test in self.guarded_common_tests:
1.224 ref = first(accessor_general_types)
1.225
1.226 + # For attribute-based tests, tentatively identify a dynamic base.
1.227 + # Such tests allow single or multiple kinds of a type.
1.228 +
1.229 elif test in self.common_tests or test in self.specific_tests:
1.230 - accessor_test_types = self.reference_test_accessor_types[location]
1.231 - ref = first(accessor_test_types)
1.232 + dynamic_base = self.reference_test_accessor_types[location]
1.233
1.234 # Static accessors.
1.235
1.236 - if ref and test in self.class_tests:
1.237 - base = ref.get_origin()
1.238 -
1.239 - # Constrained accessors that are not static but whose nature is
1.240 - # determined.
1.241 -
1.242 - if not base and ref and constrained:
1.243 + if not base and test in self.class_tests:
1.244 + base = ref and ref.get_origin() or dynamic_base
1.245 +
1.246 + # Accessors that are not static but whose nature is determined.
1.247 +
1.248 + elif not base and ref:
1.249 dynamic_base = ref.get_origin()
1.250
1.251 traversed = []