1.1 --- a/deducer.py Wed Sep 28 22:57:04 2016 +0200
1.2 +++ b/deducer.py Wed Sep 28 22:59:21 2016 +0200
1.3 @@ -24,7 +24,7 @@
1.4 CommonOutput
1.5 from encoders import encode_attrnames, encode_access_location, \
1.6 encode_constrained, encode_location, encode_usage, \
1.7 - get_kinds, test_for_kinds, test_for_types
1.8 + get_kinds, test_for_kinds, test_for_type
1.9 from os.path import join
1.10 from referencing import combine_types, is_single_class_type, separate_types, \
1.11 Reference
1.12 @@ -65,6 +65,7 @@
1.13 # Map constant accesses to redefined accesses.
1.14
1.15 self.const_accesses = {}
1.16 + self.const_accesses_rev = {}
1.17
1.18 # Map usage observations to assigned attributes.
1.19
1.20 @@ -88,11 +89,15 @@
1.21 self.provider_class_types = {}
1.22 self.provider_instance_types = {}
1.23 self.provider_module_types = {}
1.24 - self.reference_constrained = set()
1.25 + self.accessor_constrained = set()
1.26 self.access_constrained = set()
1.27 self.referenced_attrs = {}
1.28 self.referenced_objects = {}
1.29
1.30 + # Details of access operations.
1.31 +
1.32 + self.access_plans = {}
1.33 +
1.34 # Accumulated information about accessors and providers.
1.35
1.36 self.accessor_general_class_types = {}
1.37 @@ -104,14 +109,15 @@
1.38 self.accessor_guard_tests = {}
1.39
1.40 # Accumulated information about accessed attributes and
1.41 - # attribute-specific accessor tests.
1.42 + # access/attribute-specific accessor tests.
1.43
1.44 self.reference_class_attrs = {}
1.45 self.reference_instance_attrs = {}
1.46 self.reference_module_attrs = {}
1.47 self.reference_all_attrs = {}
1.48 self.reference_all_attrtypes = {}
1.49 - self.reference_all_accessors = {}
1.50 + self.reference_all_accessor_types = {}
1.51 + self.reference_all_accessor_general_types = {}
1.52 self.reference_test_types = {}
1.53 self.reference_test_accessor_types = {}
1.54
1.55 @@ -126,6 +132,7 @@
1.56 self.identify_references()
1.57 self.classify_accessors()
1.58 self.classify_accesses()
1.59 + self.initialise_access_plans()
1.60
1.61 def to_output(self):
1.62
1.63 @@ -215,7 +222,7 @@
1.64 locations.sort()
1.65
1.66 for location in locations:
1.67 - constrained = location in self.reference_constrained
1.68 + constrained = location in self.accessor_constrained
1.69
1.70 # Accessor information.
1.71
1.72 @@ -373,6 +380,27 @@
1.73 f_tests.close()
1.74 f_warnings.close()
1.75
1.76 + def write_access_plans(self):
1.77 +
1.78 + "Each attribute access is written out as a plan."
1.79 +
1.80 + f_attrs = open(join(self.output, "attribute_plans"), "w")
1.81 +
1.82 + try:
1.83 + locations = self.access_plans.keys()
1.84 + locations.sort()
1.85 +
1.86 + for location in locations:
1.87 + base, traversed, attrnames, method, test, attr = self.access_plans[location]
1.88 + print >>f_attrs, encode_access_location(location), base, \
1.89 + ".".join(traversed) or "{}", \
1.90 + ".".join(attrnames) or "{}", \
1.91 + method, test, \
1.92 + attr or "{}"
1.93 +
1.94 + finally:
1.95 + f_attrs.close()
1.96 +
1.97 def classify_accessors(self):
1.98
1.99 "For each program location, classify accessors."
1.100 @@ -383,7 +411,7 @@
1.101 locations = self.accessor_class_types.keys()
1.102
1.103 for location in locations:
1.104 - constrained = location in self.reference_constrained
1.105 + constrained = location in self.accessor_constrained
1.106
1.107 # Provider information.
1.108
1.109 @@ -425,39 +453,19 @@
1.110 # Record specific type guard details.
1.111
1.112 if len(all_types) == 1:
1.113 - self.accessor_guard_tests[location] = test_for_types("specific", all_types)
1.114 + self.accessor_guard_tests[location] = test_for_type("specific", first(all_types))
1.115 elif is_single_class_type(all_types):
1.116 self.accessor_guard_tests[location] = "specific-object"
1.117
1.118 # Record common type guard details.
1.119
1.120 elif len(all_general_types) == 1:
1.121 - self.accessor_guard_tests[location] = test_for_types("common", all_types)
1.122 + self.accessor_guard_tests[location] = test_for_type("common", first(all_types))
1.123 elif is_single_class_type(all_general_types):
1.124 self.accessor_guard_tests[location] = "common-object"
1.125
1.126 # Otherwise, no convenient guard can be defined.
1.127
1.128 - def write_access_plans(self):
1.129 -
1.130 - "Each attribute access is written out as a plan."
1.131 -
1.132 - f_attrs = open(join(self.output, "attribute_plans"), "w")
1.133 -
1.134 - try:
1.135 - locations = self.referenced_attrs.keys()
1.136 - locations.sort()
1.137 -
1.138 - for location in locations:
1.139 - base, traversed, attrnames, attr = self.get_access(location)
1.140 - print >>f_attrs, encode_access_location(location), base, \
1.141 - ".".join(traversed) or "{}", \
1.142 - ".".join(attrnames) or "{}", \
1.143 - attr or "{}"
1.144 -
1.145 - finally:
1.146 - f_attrs.close()
1.147 -
1.148 def classify_accesses(self):
1.149
1.150 "For each program location, classify accesses."
1.151 @@ -483,15 +491,12 @@
1.152 # Obtain the provider types for guard-related attribute access
1.153 # checks.
1.154
1.155 - provider_guard_types = self.provider_all_types.get(accessor_location)
1.156 - all_provider_types.update(provider_guard_types)
1.157 -
1.158 - # Obtain the accessor types.
1.159 -
1.160 - accessor_guard_types = self.accessor_all_types.get(accessor_location)
1.161 - accessor_general_guard_types = self.accessor_all_general_types.get(accessor_location)
1.162 - all_accessor_types.update(accessor_guard_types)
1.163 - all_accessor_general_types.update(accessor_general_guard_types)
1.164 + all_provider_types.update(self.provider_all_types.get(accessor_location))
1.165 +
1.166 + # Obtain the accessor guard types (specific and general).
1.167 +
1.168 + all_accessor_types.update(self.accessor_all_types.get(accessor_location))
1.169 + all_accessor_general_types.update(self.accessor_all_general_types.get(accessor_location))
1.170
1.171 # Determine the basis on which the access has been guarded.
1.172
1.173 @@ -506,7 +511,8 @@
1.174 (guard_class_types, guard_instance_types, guard_module_types,
1.175 _function_types, _var_types) = separate_types(all_provider_types)
1.176
1.177 - self.reference_all_accessors[location] = all_accessor_types
1.178 + self.reference_all_accessor_types[location] = all_accessor_types
1.179 + self.reference_all_accessor_general_types[location] = all_accessor_general_types
1.180
1.181 # Attribute information, both name-based and anonymous.
1.182
1.183 @@ -563,11 +569,11 @@
1.184
1.185 if guarded and all_accessed_attrs.issubset(guard_attrs):
1.186 if len(all_accessor_types) == 1:
1.187 - self.reference_test_types[location] = test_for_types("guarded-specific", all_accessor_types)
1.188 + self.reference_test_types[location] = test_for_type("guarded-specific", first(all_accessor_types))
1.189 elif is_single_class_type(all_accessor_types):
1.190 self.reference_test_types[location] = "guarded-specific-object"
1.191 elif len(all_accessor_general_types) == 1:
1.192 - self.reference_test_types[location] = test_for_types("guarded-common", all_accessor_general_types)
1.193 + self.reference_test_types[location] = test_for_type("guarded-common", first(all_accessor_general_types))
1.194 elif is_single_class_type(all_accessor_general_types):
1.195 self.reference_test_types[location] = "guarded-common-object"
1.196
1.197 @@ -604,6 +610,13 @@
1.198 else:
1.199 self.reference_test_types[location] = "validate"
1.200
1.201 + def initialise_access_plans(self):
1.202 +
1.203 + "Define attribute access plans."
1.204 +
1.205 + for location in self.referenced_attrs.keys():
1.206 + self.access_plans[location] = self.get_access_plan(location)
1.207 +
1.208 def get_referenced_attrs(self, location):
1.209
1.210 """
1.211 @@ -1154,6 +1167,7 @@
1.212
1.213 if original_location != access_location:
1.214 self.const_accesses[original_location] = access_location
1.215 + self.const_accesses_rev[access_location] = original_location
1.216
1.217 # Aliased name definitions. All aliases with usage will have been
1.218 # defined, but they may be refined according to referenced accesses.
1.219 @@ -1346,7 +1360,7 @@
1.220 self.init_definition_details(location)
1.221 self.record_types_for_usage(location, [attrname])
1.222
1.223 - constrained = location in self.reference_constrained and constrained
1.224 + constrained = location in self.accessor_constrained and constrained
1.225
1.226 self.init_access_details(access_location)
1.227 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained)
1.228 @@ -1567,7 +1581,7 @@
1.229 self.accessor_module_types[location].update(module_types)
1.230
1.231 if constrained:
1.232 - self.reference_constrained.add(location)
1.233 + self.accessor_constrained.add(location)
1.234
1.235 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained):
1.236
1.237 @@ -1636,37 +1650,142 @@
1.238
1.239 return attrs
1.240
1.241 - def get_access(self, location):
1.242 + guarded_specific_tests = (
1.243 + "guarded-specific-instance",
1.244 + "guarded-specific-type",
1.245 + "guarded-specific-object",
1.246 + )
1.247 +
1.248 + guarded_common_tests = (
1.249 + "guarded-common-instance",
1.250 + "guarded-common-type",
1.251 + "guarded-common-object",
1.252 + )
1.253 +
1.254 + specific_tests = (
1.255 + "specific-instance",
1.256 + "specific-type",
1.257 + "specific-object",
1.258 + )
1.259 +
1.260 + common_tests = (
1.261 + "common-instance",
1.262 + "common-type",
1.263 + "common-object",
1.264 + )
1.265 +
1.266 + class_tests = (
1.267 + "guarded-specific-type",
1.268 + "guarded-common-type",
1.269 + "specific-type",
1.270 + "common-type",
1.271 + )
1.272 +
1.273 + class_or_instance_tests = (
1.274 + "guarded-specific-object",
1.275 + "guarded-common-object",
1.276 + "specific-object",
1.277 + "common-object",
1.278 + )
1.279 +
1.280 + def get_access_plan(self, location):
1.281
1.282 "Return details of the access at the given 'location'."
1.283
1.284 - const_access = self.const_accesses.has_key(location)
1.285 - if const_access:
1.286 - location = self.const_accesses[location]
1.287 + const_access = self.const_accesses_rev.has_key(location)
1.288
1.289 path, name, attrname_str, version = location
1.290 attrnames = attrname_str.split(".")
1.291 attrname = attrnames[0]
1.292
1.293 - # Obtain only reference information.
1.294 + # Obtain reference and accessor information, retaining also distinct
1.295 + # provider kind details.
1.296
1.297 attrs = []
1.298 objtypes = []
1.299 + provider_kinds = set()
1.300 +
1.301 for attrtype, objtype, attr in self.referenced_attrs[location]:
1.302 attrs.append(attr)
1.303 objtypes.append(objtype)
1.304 -
1.305 - # Identify the last static attribute.
1.306 + provider_kinds.add(attrtype)
1.307 +
1.308 + # Obtain accessor type and kind information.
1.309 +
1.310 + accessor_types = self.reference_all_accessor_types[location]
1.311 + accessor_general_types = self.reference_all_accessor_general_types[location]
1.312 + accessor_kinds = get_kinds(accessor_general_types)
1.313 +
1.314 + # Determine any guard or test requirements.
1.315 +
1.316 + constrained = location in self.access_constrained
1.317 +
1.318 + if constrained:
1.319 + test = "constrained"
1.320 + else:
1.321 + test = self.reference_test_types[location]
1.322 +
1.323 + # Determine the accessor and provider properties.
1.324 +
1.325 + class_accessor = "<class>" in accessor_kinds
1.326 + module_accessor = "<module>" in accessor_kinds
1.327 + instance_accessor = "<instance>" in accessor_kinds
1.328 + provided_by_class = "<class>" in provider_kinds
1.329 + provided_by_instance = "<instance>" in provider_kinds
1.330 +
1.331 + # Identify the last static attribute for context acquisition.
1.332 +
1.333 + base = None
1.334 + dynamic_base = None
1.335 +
1.336 + # Constant accesses have static accessors.
1.337
1.338 if const_access:
1.339 base = len(objtypes) == 1 and first(objtypes)
1.340 +
1.341 + # Constant accessors are static.
1.342 +
1.343 else:
1.344 ref = self.importer.identify("%s.%s" % (path, name))
1.345 - if not ref:
1.346 - base = name
1.347 - else:
1.348 + if ref:
1.349 base = ref.get_origin()
1.350
1.351 + # Identify single type accessors. These can be relied upon to
1.352 + # provide attributes in the same positions.
1.353 +
1.354 + elif constrained and len(accessor_types) == 1:
1.355 + ref = first(accessor_types)
1.356 +
1.357 + elif constrained and len(accessor_general_types) == 1:
1.358 + ref = first(accessor_general_types)
1.359 +
1.360 + # Multiple type accessors may involve a single type but where both
1.361 + # the type itself (a class) or an instance of the type might be used
1.362 + # to access a class attribute.
1.363 +
1.364 + # Usage of previously-generated guard and test details.
1.365 +
1.366 + elif test in self.guarded_specific_tests:
1.367 + ref = first(accessor_types)
1.368 +
1.369 + elif test in self.guarded_common_tests:
1.370 + ref = first(accessor_general_types)
1.371 +
1.372 + elif test in self.common_tests or test in self.specific_tests:
1.373 + accessor_test_types = self.reference_test_accessor_types[location]
1.374 + ref = first(accessor_test_types)
1.375 +
1.376 + # Static accessors.
1.377 +
1.378 + if ref and test in self.class_tests:
1.379 + base = ref.get_origin()
1.380 +
1.381 + # Constrained accessors that are not static but whose nature is
1.382 + # determined.
1.383 +
1.384 + if not base and ref and constrained:
1.385 + dynamic_base = ref.get_origin()
1.386 +
1.387 traversed = []
1.388
1.389 # Traverse remaining attributes.
1.390 @@ -1680,15 +1799,51 @@
1.391 if not attrnames:
1.392 break
1.393
1.394 + # Update the last static attribute.
1.395 +
1.396 if attr.static():
1.397 base = attr.get_origin()
1.398 traversed = []
1.399
1.400 + # Get the next attribute.
1.401 +
1.402 attrname = attrnames[0]
1.403 attrs = self.importer.get_attributes(attr, attrname)
1.404 +
1.405 + # Where many attributes are suggested, no single attribute identity can
1.406 + # be loaded.
1.407 +
1.408 else:
1.409 attr = None
1.410
1.411 - return base, traversed, attrnames, attr and attr.get_name()
1.412 + # Determine the method of access.
1.413 +
1.414 + # Static, identified attribute.
1.415 +
1.416 + if attr and attr.static():
1.417 + method = "static"; origin = attr.final()
1.418 +
1.419 + # Identified attribute that must be accessed via its parent.
1.420 +
1.421 + elif attr and attr.get_name():
1.422 + method = "direct"; origin = attr.get_name()
1.423 +
1.424 + # Attribute accessed at a known position via its parent.
1.425 +
1.426 + elif base or dynamic_base:
1.427 + method = "relative"; origin = None
1.428 +
1.429 + # The fallback case is always run-time testing and access.
1.430 +
1.431 + else:
1.432 + l = []
1.433 + if class_accessor or module_accessor or provided_by_instance:
1.434 + l.append("checkobject")
1.435 + if instance_accessor and provided_by_class:
1.436 + l.append("checkclass")
1.437 + method = "+".join(l)
1.438 + origin = None
1.439 +
1.440 + return base or name, traversed, attrnames, method, test, origin
1.441
1.442 # vim: tabstop=4 expandtab shiftwidth=4