1 #!/usr/bin/env python 2 3 """ 4 Deduce types for usage observations. 5 6 Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from common import first, get_assigned_attributes, \ 23 get_attrname_from_location, get_attrnames, \ 24 get_invoked_attributes, get_name_path, init_item, \ 25 sorted_output, CommonOutput 26 from encoders import encode_access_location, \ 27 encode_constrained, encode_location, encode_usage, \ 28 get_kinds, test_label_for_kind, test_label_for_type 29 from errors import DeduceError 30 from os.path import join 31 from referencing import combine_types, is_single_class_type, separate_types, \ 32 Reference 33 34 class Deducer(CommonOutput): 35 36 "Deduce types in a program." 37 38 def __init__(self, importer, output): 39 40 """ 41 Initialise an instance using the given 'importer' that will perform 42 deductions on the program information, writing the results to the given 43 'output' directory. 44 """ 45 46 self.importer = importer 47 self.output = output 48 49 # Descendants of classes. 50 51 self.descendants = {} 52 self.init_descendants() 53 self.init_special_attributes() 54 55 # Map locations to usage in order to determine specific types. 56 57 self.location_index = {} 58 59 # Map access locations to definition locations. 60 61 self.access_index = {} 62 63 # Map aliases to accesses that define them. 64 65 self.alias_index = {} 66 67 # Map constant accesses to redefined accesses. 68 69 self.const_accesses = {} 70 self.const_accesses_rev = {} 71 72 # Map usage observations to assigned attributes. 73 74 self.assigned_attrs = {} 75 76 # Map usage observations to objects. 77 78 self.attr_class_types = {} 79 self.attr_instance_types = {} 80 self.attr_module_types = {} 81 82 # All known attribute names. 83 84 self.all_attrnames = set() 85 86 # Modified attributes from usage observations. 87 88 self.modified_attributes = {} 89 90 # Accesses that are assignments or invocations. 91 92 self.reference_assignments = set() 93 self.reference_invocations = set() 94 95 # Map locations to types, constrained indicators and attributes. 96 97 self.accessor_class_types = {} 98 self.accessor_instance_types = {} 99 self.accessor_module_types = {} 100 self.provider_class_types = {} 101 self.provider_instance_types = {} 102 self.provider_module_types = {} 103 self.accessor_constrained = set() 104 self.access_constrained = set() 105 self.referenced_attrs = {} 106 self.referenced_objects = {} 107 108 # Details of access operations. 109 110 self.access_plans = {} 111 112 # Accumulated information about accessors and providers. 113 114 self.accessor_general_class_types = {} 115 self.accessor_general_instance_types = {} 116 self.accessor_general_module_types = {} 117 self.accessor_all_types = {} 118 self.accessor_all_general_types = {} 119 self.provider_all_types = {} 120 self.accessor_guard_tests = {} 121 122 # Accumulated information about accessed attributes and 123 # access/attribute-specific accessor tests. 124 125 self.reference_all_attrs = {} 126 self.reference_all_attrtypes = {} 127 self.reference_all_accessor_types = {} 128 self.reference_all_accessor_general_types = {} 129 self.reference_test_types = {} 130 self.reference_test_accessor_type = {} 131 132 # The processing workflow itself. 133 134 self.init_usage_index() 135 self.init_attr_type_indexes() 136 self.init_combined_attribute_index() 137 self.init_accessors() 138 self.init_accesses() 139 self.init_aliases() 140 self.modify_mutated_attributes() 141 self.identify_references() 142 self.classify_accessors() 143 self.classify_accesses() 144 self.initialise_access_plans() 145 self.identify_dependencies() 146 147 def to_output(self): 148 149 "Write the output files using deduction information." 150 151 self.check_output() 152 153 self.write_mutations() 154 self.write_accessors() 155 self.write_accesses() 156 self.write_access_plans() 157 158 def write_mutations(self): 159 160 """ 161 Write mutation-related output in the following format: 162 163 qualified name " " original object type 164 165 Object type can be "<class>", "<function>" or "<var>". 166 """ 167 168 f = open(join(self.output, "mutations"), "w") 169 try: 170 attrs = self.modified_attributes.items() 171 attrs.sort() 172 173 for attr, value in attrs: 174 print >>f, attr, value 175 finally: 176 f.close() 177 178 def write_accessors(self): 179 180 """ 181 Write reference-related output in the following format for types: 182 183 location " " ( "constrained" | "deduced" ) " " attribute type " " most general type names " " number of specific types 184 185 Note that multiple lines can be given for each location, one for each 186 attribute type. 187 188 Locations have the following format: 189 190 qualified name of scope "." local name ":" name version 191 192 The attribute type can be "<class>", "<instance>", "<module>" or "<>", 193 where the latter indicates an absence of suitable references. 194 195 Type names indicate the type providing the attributes, being either a 196 class or module qualified name. 197 198 ---- 199 200 A summary of accessor types is formatted as follows: 201 202 location " " ( "constrained" | "deduced" ) " " ( "specific" | "common" | "unguarded" ) " " most general type names " " number of specific types 203 204 This summary groups all attribute types (class, instance, module) into a 205 single line in order to determine the complexity of identifying an 206 accessor. 207 208 ---- 209 210 References that cannot be supported by any types are written to a 211 warnings file in the following format: 212 213 location 214 215 ---- 216 217 For each location where a guard would be asserted to guarantee the 218 nature of an object, the following format is employed: 219 220 location " " ( "specific" | "common" ) " " object kind " " object types 221 222 Object kind can be "<class>", "<instance>" or "<module>". 223 """ 224 225 f_type_summary = open(join(self.output, "type_summary"), "w") 226 f_types = open(join(self.output, "types"), "w") 227 f_warnings = open(join(self.output, "type_warnings"), "w") 228 f_guards = open(join(self.output, "guards"), "w") 229 230 try: 231 locations = self.accessor_class_types.keys() 232 locations.sort() 233 234 for location in locations: 235 constrained = location in self.accessor_constrained 236 237 # Accessor information. 238 239 class_types = self.accessor_class_types[location] 240 instance_types = self.accessor_instance_types[location] 241 module_types = self.accessor_module_types[location] 242 243 general_class_types = self.accessor_general_class_types[location] 244 general_instance_types = self.accessor_general_instance_types[location] 245 general_module_types = self.accessor_general_module_types[location] 246 247 all_types = self.accessor_all_types[location] 248 all_general_types = self.accessor_all_general_types[location] 249 250 if class_types: 251 print >>f_types, encode_location(location), encode_constrained(constrained), "<class>", \ 252 sorted_output(general_class_types), len(class_types) 253 254 if instance_types: 255 print >>f_types, encode_location(location), encode_constrained(constrained), "<instance>", \ 256 sorted_output(general_instance_types), len(instance_types) 257 258 if module_types: 259 print >>f_types, encode_location(location), encode_constrained(constrained), "<module>", \ 260 sorted_output(general_module_types), len(module_types) 261 262 if not all_types: 263 print >>f_types, encode_location(location), "deduced", "<>", 0 264 attrnames = list(self.location_index[location]) 265 attrnames.sort() 266 print >>f_warnings, encode_location(location), "; ".join(map(encode_usage, attrnames)) 267 268 guard_test = self.accessor_guard_tests.get(location) 269 if guard_test: 270 guard_test_type, guard_test_arg = guard_test 271 272 # Write specific type guard details. 273 274 if guard_test and guard_test_type == "specific": 275 print >>f_guards, encode_location(location), "-".join(guard_test), \ 276 first(get_kinds(all_types)), \ 277 sorted_output(all_types) 278 279 # Write common type guard details. 280 281 elif guard_test and guard_test_type == "common": 282 print >>f_guards, encode_location(location), "-".join(guard_test), \ 283 first(get_kinds(all_general_types)), \ 284 sorted_output(all_general_types) 285 286 print >>f_type_summary, encode_location(location), encode_constrained(constrained), \ 287 guard_test and "-".join(guard_test) or "unguarded", sorted_output(all_general_types), len(all_types) 288 289 finally: 290 f_type_summary.close() 291 f_types.close() 292 f_warnings.close() 293 f_guards.close() 294 295 def write_accesses(self): 296 297 """ 298 Specific attribute output is produced in the following format: 299 300 location " " ( "constrained" | "deduced" ) " " attribute type " " attribute references 301 302 Note that multiple lines can be given for each location and attribute 303 name, one for each attribute type. 304 305 Locations have the following format: 306 307 qualified name of scope "." local name " " attribute name ":" access number 308 309 The attribute type can be "<class>", "<instance>", "<module>" or "<>", 310 where the latter indicates an absence of suitable references. 311 312 Attribute references have the following format: 313 314 object type ":" qualified name 315 316 Object type can be "<class>", "<function>" or "<var>". 317 318 ---- 319 320 A summary of attributes is formatted as follows: 321 322 location " " attribute name " " ( "constrained" | "deduced" ) " " test " " attribute references 323 324 This summary groups all attribute types (class, instance, module) into a 325 single line in order to determine the complexity of each access. 326 327 Tests can be "validate", "specific", "untested", "guarded-validate" or "guarded-specific". 328 329 ---- 330 331 For each access where a test would be asserted to guarantee the 332 nature of an attribute, the following formats are employed: 333 334 location " " attribute name " " "validate" 335 location " " attribute name " " "specific" " " attribute type " " object type 336 337 ---- 338 339 References that cannot be supported by any types are written to a 340 warnings file in the following format: 341 342 location 343 """ 344 345 f_attr_summary = open(join(self.output, "attribute_summary"), "w") 346 f_attrs = open(join(self.output, "attributes"), "w") 347 f_tests = open(join(self.output, "tests"), "w") 348 f_warnings = open(join(self.output, "attribute_warnings"), "w") 349 350 try: 351 locations = self.referenced_attrs.keys() 352 locations.sort() 353 354 for location in locations: 355 constrained = location in self.access_constrained 356 357 # Attribute information, both name-based and anonymous. 358 359 referenced_attrs = self.referenced_attrs[location] 360 361 if referenced_attrs: 362 attrname = get_attrname_from_location(location) 363 364 all_accessed_attrs = self.reference_all_attrs[location] 365 366 for attrtype, attrs in self.get_referenced_attrs(location): 367 print >>f_attrs, encode_access_location(location), encode_constrained(constrained), attrtype, sorted_output(attrs) 368 369 test_type = self.reference_test_types.get(location) 370 371 # Write the need to test at run time. 372 373 if test_type[0] == "validate": 374 print >>f_tests, encode_access_location(location), "-".join(test_type) 375 376 # Write any type checks for anonymous accesses. 377 378 elif test_type and self.reference_test_accessor_type.get(location): 379 print >>f_tests, encode_access_location(location), "-".join(test_type), \ 380 sorted_output(all_accessed_attrs), \ 381 self.reference_test_accessor_type[location] 382 383 print >>f_attr_summary, encode_access_location(location), encode_constrained(constrained), \ 384 test_type and "-".join(test_type) or "untested", sorted_output(all_accessed_attrs) 385 386 else: 387 print >>f_warnings, encode_access_location(location) 388 389 finally: 390 f_attr_summary.close() 391 f_attrs.close() 392 f_tests.close() 393 f_warnings.close() 394 395 def write_access_plans(self): 396 397 """ 398 Each attribute access is written out as a plan of the following form: 399 400 location " " name " " test " " test type " " base " " traversed attributes 401 " " attributes to traverse " " context " " access method 402 " " static attribute " " accessor kinds 403 """ 404 405 f_attrs = open(join(self.output, "attribute_plans"), "w") 406 407 try: 408 locations = self.access_plans.keys() 409 locations.sort() 410 411 for location in locations: 412 name, test, test_type, base, \ 413 traversed, traversal_modes, attrnames, \ 414 context, context_test, \ 415 first_method, final_method, \ 416 attr, accessor_kinds = self.access_plans[location] 417 418 print >>f_attrs, encode_access_location(location), \ 419 name or "{}", \ 420 test and "-".join(test) or "{}", \ 421 test_type or "{}", \ 422 base or "{}", \ 423 ".".join(traversed) or "{}", \ 424 ".".join(traversal_modes) or "{}", \ 425 ".".join(attrnames) or "{}", \ 426 context, context_test, \ 427 first_method, final_method, attr or "{}", \ 428 ",".join(accessor_kinds) 429 430 finally: 431 f_attrs.close() 432 433 def classify_accessors(self): 434 435 "For each program location, classify accessors." 436 437 # Where instance and module types are defined, class types are also 438 # defined. See: init_definition_details 439 440 locations = self.accessor_class_types.keys() 441 442 for location in locations: 443 constrained = location in self.accessor_constrained 444 445 # Provider information. 446 447 class_types = self.provider_class_types[location] 448 instance_types = self.provider_instance_types[location] 449 module_types = self.provider_module_types[location] 450 451 # Collect specific and general type information. 452 453 self.provider_all_types[location] = \ 454 combine_types(class_types, instance_types, module_types) 455 456 # Accessor information. 457 458 class_types = self.accessor_class_types[location] 459 self.accessor_general_class_types[location] = \ 460 general_class_types = self.get_most_general_class_types(class_types) 461 462 instance_types = self.accessor_instance_types[location] 463 self.accessor_general_instance_types[location] = \ 464 general_instance_types = self.get_most_general_class_types(instance_types) 465 466 module_types = self.accessor_module_types[location] 467 self.accessor_general_module_types[location] = \ 468 general_module_types = self.get_most_general_module_types(module_types) 469 470 # Collect specific and general type information. 471 472 self.accessor_all_types[location] = all_types = \ 473 combine_types(class_types, instance_types, module_types) 474 475 self.accessor_all_general_types[location] = all_general_types = \ 476 combine_types(general_class_types, general_instance_types, general_module_types) 477 478 # Record guard information. 479 480 if not constrained: 481 482 # Record specific type guard details. 483 484 if len(all_types) == 1: 485 self.accessor_guard_tests[location] = ("specific", test_label_for_type(first(all_types))) 486 elif is_single_class_type(all_types): 487 self.accessor_guard_tests[location] = ("specific", "object") 488 489 # Record common type guard details. 490 491 elif len(all_general_types) == 1: 492 self.accessor_guard_tests[location] = ("common", test_label_for_type(first(all_types))) 493 elif is_single_class_type(all_general_types): 494 self.accessor_guard_tests[location] = ("common", "object") 495 496 # Otherwise, no convenient guard can be defined. 497 498 def classify_accesses(self): 499 500 "For each program location, classify accesses." 501 502 # Attribute accesses use potentially different locations to those of 503 # accessors. 504 505 locations = self.referenced_attrs.keys() 506 507 for location in locations: 508 constrained = location in self.access_constrained 509 510 # Combine type information from all accessors supplying the access. 511 512 accessor_locations = self.get_accessors_for_access(location) 513 514 all_provider_types = set() 515 all_accessor_types = set() 516 all_accessor_general_types = set() 517 518 for accessor_location in accessor_locations: 519 520 # Obtain the provider types for guard-related attribute access 521 # checks. 522 523 all_provider_types.update(self.provider_all_types.get(accessor_location)) 524 525 # Obtain the accessor guard types (specific and general). 526 527 all_accessor_types.update(self.accessor_all_types.get(accessor_location)) 528 all_accessor_general_types.update(self.accessor_all_general_types.get(accessor_location)) 529 530 # Obtain basic properties of the types involved in the access. 531 532 single_accessor_type = len(all_accessor_types) == 1 533 single_accessor_class_type = is_single_class_type(all_accessor_types) 534 single_accessor_general_type = len(all_accessor_general_types) == 1 535 single_accessor_general_class_type = is_single_class_type(all_accessor_general_types) 536 537 # Determine whether the attribute access is guarded or not. 538 539 guarded = ( 540 single_accessor_type or single_accessor_class_type or 541 single_accessor_general_type or single_accessor_general_class_type 542 ) 543 544 if guarded: 545 (guard_class_types, guard_instance_types, guard_module_types, 546 _function_types, _var_types) = separate_types(all_provider_types) 547 548 self.reference_all_accessor_types[location] = all_accessor_types 549 self.reference_all_accessor_general_types[location] = all_accessor_general_types 550 551 # Attribute information, both name-based and anonymous. 552 553 referenced_attrs = self.referenced_attrs[location] 554 555 if not referenced_attrs: 556 raise DeduceError("In %s, access via %s to attribute %s (occurrence %d) cannot be identified." % location) 557 558 # Record attribute information for each name used on the 559 # accessor. 560 561 attrname = get_attrname_from_location(location) 562 563 all_accessed_attrs = set() 564 all_providers = set() 565 566 # Obtain provider and attribute details for this kind of 567 # object. 568 569 for attrtype, object_type, attr in referenced_attrs: 570 all_accessed_attrs.add(attr) 571 all_providers.add(object_type) 572 573 all_general_providers = self.get_most_general_types(all_providers) 574 575 # Determine which attributes would be provided by the 576 # accessor types upheld by a guard. 577 578 if guarded: 579 guard_attrs = set() 580 for _attrtype, object_type, attr in \ 581 self._identify_reference_attribute(attrname, guard_class_types, guard_instance_types, guard_module_types): 582 guard_attrs.add(attr) 583 else: 584 guard_attrs = None 585 586 self.reference_all_attrs[location] = all_accessed_attrs 587 588 # Constrained accesses guarantee the nature of the accessor. 589 # However, there may still be many types involved. 590 591 if constrained: 592 if single_accessor_type: 593 self.reference_test_types[location] = ("constrained", "specific", test_label_for_type(first(all_accessor_types))) 594 elif single_accessor_class_type: 595 self.reference_test_types[location] = ("constrained", "specific", "object") 596 elif single_accessor_general_type: 597 self.reference_test_types[location] = ("constrained", "common", test_label_for_type(first(all_accessor_general_types))) 598 elif single_accessor_general_class_type: 599 self.reference_test_types[location] = ("constrained", "common", "object") 600 else: 601 self.reference_test_types[location] = ("constrained", "many") 602 603 # Suitably guarded accesses, where the nature of the 604 # accessor can be guaranteed, do not require the attribute 605 # involved to be validated. Otherwise, for unguarded 606 # accesses, access-level tests are required. 607 608 elif guarded and all_accessed_attrs.issubset(guard_attrs): 609 if single_accessor_type: 610 self.reference_test_types[location] = ("guarded", "specific", test_label_for_type(first(all_accessor_types))) 611 elif single_accessor_class_type: 612 self.reference_test_types[location] = ("guarded", "specific", "object") 613 elif single_accessor_general_type: 614 self.reference_test_types[location] = ("guarded", "common", test_label_for_type(first(all_accessor_general_types))) 615 elif single_accessor_general_class_type: 616 self.reference_test_types[location] = ("guarded", "common", "object") 617 618 # Record the need to test the type of anonymous and 619 # unconstrained accessors. 620 621 elif len(all_providers) == 1: 622 provider = first(all_providers) 623 if provider != '__builtins__.object': 624 all_accessor_kinds = set(get_kinds(all_accessor_types)) 625 if len(all_accessor_kinds) == 1: 626 test_type = ("test", "specific", test_label_for_kind(first(all_accessor_kinds))) 627 else: 628 test_type = ("test", "specific", "object") 629 self.reference_test_types[location] = test_type 630 self.reference_test_accessor_type[location] = provider 631 632 elif len(all_general_providers) == 1: 633 provider = first(all_general_providers) 634 if provider != '__builtins__.object': 635 all_accessor_kinds = set(get_kinds(all_accessor_general_types)) 636 if len(all_accessor_kinds) == 1: 637 test_type = ("test", "common", test_label_for_kind(first(all_accessor_kinds))) 638 else: 639 test_type = ("test", "common", "object") 640 self.reference_test_types[location] = test_type 641 self.reference_test_accessor_type[location] = provider 642 643 # Record the need to test the identity of the attribute. 644 645 else: 646 self.reference_test_types[location] = ("validate",) 647 648 def initialise_access_plans(self): 649 650 "Define attribute access plans." 651 652 for location in self.referenced_attrs.keys(): 653 original_location = self.const_accesses_rev.get(location) 654 self.access_plans[original_location or location] = self.get_access_plan(location) 655 656 def identify_dependencies(self): 657 658 "Introduce more module dependencies to the importer." 659 660 for location, referenced_attrs in self.referenced_attrs.items(): 661 path, name, attrnames, version = location 662 663 # Identify module-level paths. 664 665 if self.importer.modules.has_key(path): 666 module_name = path 667 668 # Identify the module containing other paths. 669 670 else: 671 ref = self.importer.identify(path) 672 for objpath in ref.ancestors(): 673 if self.importer.modules.has_key(objpath): 674 module_name = objpath 675 break 676 else: 677 raise DeduceError("Cannot find module for path %s." % path) 678 679 # Identify references providing dependencies. 680 681 for attrtype, objtype, attr in referenced_attrs: 682 self.importer.add_dependency(path, attr.get_origin()) 683 684 def get_referenced_attrs(self, location): 685 686 """ 687 Return attributes referenced at the given access 'location' by the given 688 'attrname' as a list of (attribute type, attribute set) tuples. 689 """ 690 691 d = {} 692 for attrtype, objtype, attr in self.referenced_attrs[location]: 693 init_item(d, attrtype, set) 694 d[attrtype].add(attr.unaliased()) 695 l = d.items() 696 l.sort() # class, module, instance 697 return l 698 699 # Initialisation methods. 700 701 def init_descendants(self): 702 703 "Identify descendants of each class." 704 705 for name in self.importer.classes.keys(): 706 self.get_descendants_for_class(name) 707 708 def get_descendants_for_class(self, name): 709 710 """ 711 Use subclass information to deduce the descendants for the class of the 712 given 'name'. 713 """ 714 715 if not self.descendants.has_key(name): 716 descendants = set() 717 718 for subclass in self.importer.subclasses[name]: 719 descendants.update(self.get_descendants_for_class(subclass)) 720 descendants.add(subclass) 721 722 self.descendants[name] = descendants 723 724 return self.descendants[name] 725 726 def init_special_attributes(self): 727 728 "Add special attributes to the classes for inheritance-related tests." 729 730 all_class_attrs = self.importer.all_class_attrs 731 732 for name, descendants in self.descendants.items(): 733 for descendant in descendants: 734 all_class_attrs[descendant]["#%s" % name] = name 735 736 for name in all_class_attrs.keys(): 737 all_class_attrs[name]["#%s" % name] = name 738 739 def init_usage_index(self): 740 741 """ 742 Create indexes for module and function attribute usage and for anonymous 743 accesses. 744 """ 745 746 for module in self.importer.get_modules(): 747 for path, assignments in module.attr_usage.items(): 748 self.add_usage(assignments, path) 749 750 for location, all_attrnames in self.importer.all_attr_accesses.items(): 751 for attrnames in all_attrnames: 752 attrname = get_attrnames(attrnames)[-1] 753 access_location = (location, None, attrnames, 0) 754 self.add_usage_term(access_location, ((attrname, False, False),)) 755 756 def add_usage(self, assignments, path): 757 758 """ 759 Collect usage from the given 'assignments', adding 'path' details to 760 each record if specified. Add the usage to an index mapping to location 761 information, as well as to an index mapping locations to usages. 762 """ 763 764 for name, versions in assignments.items(): 765 for i, usages in enumerate(versions): 766 location = (path, name, None, i) 767 768 for usage in usages: 769 self.add_usage_term(location, usage) 770 771 def add_usage_term(self, location, usage): 772 773 """ 774 For 'location' and using 'usage' as a description of usage, record 775 in the usage index a mapping from the usage to 'location', and record in 776 the location index a mapping from 'location' to the usage. 777 """ 778 779 init_item(self.location_index, location, set) 780 self.location_index[location].add(usage) 781 782 def init_accessors(self): 783 784 "Create indexes for module and function accessor information." 785 786 for module in self.importer.get_modules(): 787 for path, all_accesses in module.attr_accessors.items(): 788 self.add_accessors(all_accesses, path) 789 790 def add_accessors(self, all_accesses, path): 791 792 """ 793 For attribute accesses described by the mapping of 'all_accesses' from 794 name details to accessor details, record the locations of the accessors 795 for each access. 796 """ 797 798 # Get details for each access combining the given name and attribute. 799 800 for (name, attrnames), accesses in all_accesses.items(): 801 802 # Obtain the usage details using the access information. 803 804 for access_number, versions in enumerate(accesses): 805 access_location = (path, name, attrnames, access_number) 806 locations = [] 807 808 for version in versions: 809 location = (path, name, None, version) 810 locations.append(location) 811 812 self.access_index[access_location] = locations 813 814 def get_accessors_for_access(self, access_location): 815 816 "Find a definition providing accessor details, if necessary." 817 818 try: 819 return self.access_index[access_location] 820 except KeyError: 821 return [access_location] 822 823 def init_accesses(self): 824 825 """ 826 Check that attributes used in accesses are actually defined on some 827 object. This can be overlooked if unknown attributes are employed in 828 attribute chains. 829 830 Initialise collections for accesses involving assignments. 831 """ 832 833 # For each scope, obtain access details. 834 835 for path, all_accesses in self.importer.all_attr_access_modifiers.items(): 836 837 # For each combination of name and attribute names, obtain 838 # applicable modifiers. 839 840 for (name, attrname_str), modifiers in all_accesses.items(): 841 842 # For each access, determine the name versions affected by 843 # assignments. 844 845 for access_number, (assignment, invocation) in enumerate(modifiers): 846 847 if name: 848 access_location = (path, name, attrname_str, access_number) 849 else: 850 access_location = (path, None, attrname_str, 0) 851 852 # Plain name accesses do not employ attributes and are 853 # ignored. 854 855 if not attrname_str: 856 continue 857 858 attrnames = get_attrnames(attrname_str) 859 860 # Check the attribute names. 861 862 for attrname in attrnames: 863 if not attrname in self.all_attrnames: 864 raise DeduceError("In %s, attribute %s is not defined in the program." % (path, attrname)) 865 866 # Now only process assignments and invocations. 867 868 if invocation: 869 self.reference_invocations.add(access_location) 870 continue 871 elif not assignment: 872 continue 873 874 # Associate assignments with usage. 875 876 self.reference_assignments.add(access_location) 877 878 # Assignment occurs for the only attribute. 879 880 if len(attrnames) == 1: 881 accessor_locations = self.get_accessors_for_access(access_location) 882 883 for location in accessor_locations: 884 for usage in self.location_index[location]: 885 init_item(self.assigned_attrs, usage, set) 886 self.assigned_attrs[usage].add((path, name, attrnames[0])) 887 888 # Assignment occurs for the final attribute. 889 890 else: 891 usage = ((attrnames[-1], False, False),) 892 init_item(self.assigned_attrs, usage, set) 893 self.assigned_attrs[usage].add((path, name, attrnames[-1])) 894 895 def init_aliases(self): 896 897 "Expand aliases so that alias-based accesses can be resolved." 898 899 # Get aliased names with details of their accesses. 900 901 for name_path, all_aliases in self.importer.all_aliased_names.items(): 902 path, name = name_path.rsplit(".", 1) 903 904 # For each version of the name, obtain the access location. 905 906 for version, (original_name, attrnames, access_number) in all_aliases.items(): 907 accessor_location = (path, name, None, version) 908 access_location = (path, original_name, attrnames, access_number) 909 init_item(self.alias_index, accessor_location, list) 910 self.alias_index[accessor_location].append(access_location) 911 912 # Get aliases in terms of non-aliases and accesses. 913 914 for accessor_location, access_locations in self.alias_index.items(): 915 self.update_aliases(accessor_location, access_locations) 916 917 def update_aliases(self, accessor_location, access_locations, visited=None): 918 919 """ 920 Update the given 'accessor_location' defining an alias, update 921 'access_locations' to refer to non-aliases, following name references 922 via the access index. 923 924 If 'visited' is specified, it contains a set of accessor locations (and 925 thus keys to the alias index) that are currently being defined. 926 """ 927 928 if visited is None: 929 visited = set() 930 931 updated_locations = set() 932 933 for access_location in access_locations: 934 (path, original_name, attrnames, access_number) = access_location 935 936 # Where an alias refers to a name access, obtain the original name 937 # version details. 938 939 if attrnames is None: 940 941 # For each name version, attempt to determine any accesses that 942 # initialise the name. 943 944 for name_accessor_location in self.access_index[access_location]: 945 946 # Already-visited aliases do not contribute details. 947 948 if name_accessor_location in visited: 949 continue 950 951 visited.add(name_accessor_location) 952 953 name_access_locations = self.alias_index.get(name_accessor_location) 954 if name_access_locations: 955 updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited)) 956 else: 957 updated_locations.add(name_accessor_location) 958 959 # Otherwise, record the access details. 960 961 else: 962 updated_locations.add(access_location) 963 964 self.alias_index[accessor_location] = updated_locations 965 return updated_locations 966 967 # Attribute mutation for types. 968 969 def modify_mutated_attributes(self): 970 971 "Identify known, mutated attributes and change their state." 972 973 # Usage-based accesses. 974 975 for usage, all_attrnames in self.assigned_attrs.items(): 976 if not usage: 977 continue 978 979 for path, name, attrname in all_attrnames: 980 class_types = self.get_class_types_for_usage(usage) 981 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 982 module_types = self.get_module_types_for_usage(usage) 983 984 # Detect self usage within methods in order to narrow the scope 985 # of the mutation. 986 987 t = name == "self" and self.constrain_self_reference(path, class_types, only_instance_types) 988 if t: 989 class_types, only_instance_types, module_types, constrained = t 990 objects = set(class_types).union(only_instance_types).union(module_types) 991 992 self.mutate_attribute(objects, attrname) 993 994 def mutate_attribute(self, objects, attrname): 995 996 "Mutate static 'objects' with the given 'attrname'." 997 998 for name in objects: 999 attr = "%s.%s" % (name, attrname) 1000 value = self.importer.get_object(attr) 1001 1002 # If the value is None, the attribute is 1003 # inherited and need not be set explicitly on 1004 # the class concerned. 1005 1006 if value: 1007 self.modified_attributes[attr] = value 1008 self.importer.set_object(attr, value.as_var()) 1009 1010 # Simplification of types. 1011 1012 def get_most_general_types(self, types): 1013 1014 "Return the most general types for the given 'types'." 1015 1016 module_types = set() 1017 class_types = set() 1018 1019 for type in types: 1020 ref = self.importer.identify(type) 1021 if ref.has_kind("<module>"): 1022 module_types.add(type) 1023 else: 1024 class_types.add(type) 1025 1026 types = set(self.get_most_general_module_types(module_types)) 1027 types.update(self.get_most_general_class_types(class_types)) 1028 return types 1029 1030 def get_most_general_class_types(self, class_types): 1031 1032 "Return the most general types for the given 'class_types'." 1033 1034 class_types = set(class_types) 1035 to_remove = set() 1036 1037 for class_type in class_types: 1038 for base in self.importer.classes[class_type]: 1039 base = base.get_origin() 1040 descendants = self.descendants[base] 1041 if base in class_types and descendants.issubset(class_types): 1042 to_remove.update(descendants) 1043 1044 class_types.difference_update(to_remove) 1045 return class_types 1046 1047 def get_most_general_module_types(self, module_types): 1048 1049 "Return the most general type for the given 'module_types'." 1050 1051 # Where all modules are provided, an object would provide the same 1052 # attributes. 1053 1054 if len(module_types) == len(self.importer.modules): 1055 return ["__builtins__.object"] 1056 else: 1057 return module_types 1058 1059 # More efficient usage-to-type indexing and retrieval. 1060 1061 def init_attr_type_indexes(self): 1062 1063 "Identify the types that can support each attribute name." 1064 1065 self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs) 1066 self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True) 1067 self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False) 1068 self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs) 1069 1070 def _init_attr_type_index(self, attr_types, attrs, assignment=None): 1071 1072 """ 1073 Initialise the 'attr_types' attribute-to-types mapping using the given 1074 'attrs' type-to-attributes mapping. 1075 """ 1076 1077 for name, attrnames in attrs.items(): 1078 for attrname in attrnames: 1079 1080 # Permit general access for certain kinds of object. 1081 1082 if assignment is None: 1083 init_item(attr_types, (attrname, False), set) 1084 init_item(attr_types, (attrname, True), set) 1085 attr_types[(attrname, False)].add(name) 1086 attr_types[(attrname, True)].add(name) 1087 1088 # Restrict attribute assignment for instances. 1089 1090 else: 1091 init_item(attr_types, (attrname, assignment), set) 1092 attr_types[(attrname, assignment)].add(name) 1093 1094 def get_class_types_for_usage(self, usage): 1095 1096 "Return names of classes supporting the given 'usage'." 1097 1098 return self._get_types_for_usage(usage, self.attr_class_types, self.importer.all_class_attrs) 1099 1100 def get_instance_types_for_usage(self, usage): 1101 1102 """ 1103 Return names of classes whose instances support the given 'usage' 1104 (as either class or instance attributes). 1105 """ 1106 1107 return self._get_types_for_usage(usage, self.attr_instance_types, self.importer.all_combined_attrs) 1108 1109 def get_module_types_for_usage(self, usage): 1110 1111 "Return names of modules supporting the given 'usage'." 1112 1113 return self._get_types_for_usage(usage, self.attr_module_types, self.importer.all_module_attrs) 1114 1115 def _get_types_for_usage(self, usage, attr_types, attrs): 1116 1117 """ 1118 For the given 'usage' representing attribute usage, return types 1119 recorded in the 'attr_types' attribute-to-types mapping that support 1120 such usage, with the given 'attrs' type-to-attributes mapping used to 1121 quickly assess whether a type supports all of the stated attributes. 1122 """ 1123 1124 # Where no attributes are used, any type would be acceptable. 1125 1126 if not usage: 1127 return attrs.keys() 1128 1129 keys = [] 1130 for attrname, invocation, assignment in usage: 1131 keys.append((attrname, assignment)) 1132 1133 # Obtain types supporting the first (attribute name, assignment) key... 1134 1135 types = set(attr_types.get(keys[0]) or []) 1136 1137 for key in keys[1:]: 1138 1139 # Record types that support all of the other attributes as well. 1140 1141 types.intersection_update(attr_types.get(key) or []) 1142 1143 return types 1144 1145 def init_combined_attribute_index(self): 1146 1147 "Initialise a combined index for the detection of invalid attributes." 1148 1149 self.all_attrnames = set() 1150 for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs): 1151 for name, attrnames in attrs.items(): 1152 self.all_attrnames.update(attrnames) 1153 1154 # Reference identification. 1155 1156 def identify_references(self): 1157 1158 "Identify references using usage and name reference information." 1159 1160 # Names with associated attribute usage. 1161 1162 for location, usages in self.location_index.items(): 1163 1164 # Obtain attribute usage associated with a name, deducing the nature 1165 # of the name. Obtain types only for branches involving attribute 1166 # usage. (In the absence of usage, any type could be involved, but 1167 # then no accesses exist to require knowledge of the type.) 1168 1169 have_usage = False 1170 have_no_usage_branch = False 1171 1172 for usage in usages: 1173 if not usage: 1174 have_no_usage_branch = True 1175 continue 1176 elif not have_usage: 1177 self.init_definition_details(location) 1178 have_usage = True 1179 self.record_types_for_usage(location, usage) 1180 1181 # Where some usage occurs, but where branches without usage also 1182 # occur, record the types for those branches anyway. 1183 1184 if have_usage and have_no_usage_branch: 1185 self.init_definition_details(location) 1186 self.record_types_for_usage(location, None) 1187 1188 # Specific name-based attribute accesses. 1189 1190 alias_accesses = set() 1191 1192 for access_location, accessor_locations in self.access_index.items(): 1193 self.record_types_for_access(access_location, accessor_locations, alias_accesses) 1194 1195 # Anonymous references with attribute chains. 1196 1197 for location, accesses in self.importer.all_attr_accesses.items(): 1198 1199 # Get distinct attribute names. 1200 1201 all_attrnames = set() 1202 1203 for attrnames in accesses: 1204 all_attrnames.update(get_attrnames(attrnames)) 1205 1206 # Get attribute and accessor details for each attribute name. 1207 1208 for attrname in all_attrnames: 1209 access_location = (location, None, attrname, 0) 1210 self.record_types_for_attribute(access_location, attrname) 1211 1212 # References via constant/identified objects. 1213 1214 for location, name_accesses in self.importer.all_const_accesses.items(): 1215 1216 # A mapping from the original name and attributes to resolved access 1217 # details. 1218 1219 for original_access, access in name_accesses.items(): 1220 original_name, original_attrnames = original_access 1221 objpath, ref, attrnames = access 1222 1223 # Build an accessor combining the name and attribute names used. 1224 1225 original_accessor = tuple([original_name] + original_attrnames.split(".")) 1226 1227 # Direct accesses to attributes. 1228 1229 if not attrnames: 1230 1231 # Build a descriptive location based on the original 1232 # details, exposing the final attribute name. 1233 1234 oa, attrname = original_accessor[:-1], original_accessor[-1] 1235 oa = ".".join(oa) 1236 1237 access_location = (location, oa, attrname, 0) 1238 accessor_location = (location, oa, None, 0) 1239 self.access_index[access_location] = [accessor_location] 1240 1241 self.init_access_details(access_location) 1242 self.init_definition_details(accessor_location) 1243 1244 # Obtain a reference for the accessor in order to properly 1245 # determine its type. 1246 1247 if ref.get_kind() != "<instance>": 1248 objpath = ref.get_origin() 1249 1250 objpath = objpath.rsplit(".", 1)[0] 1251 1252 # Where the object name conflicts with the module 1253 # providing it, obtain the module details. 1254 1255 if objpath in self.importer.modules: 1256 accessor = Reference("<module>", objpath) 1257 else: 1258 accessor = self.importer.get_object(objpath) 1259 1260 self.referenced_attrs[access_location] = [(accessor.get_kind(), accessor.get_origin(), ref)] 1261 self.access_constrained.add(access_location) 1262 1263 class_types, instance_types, module_types = accessor.get_types() 1264 self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True) 1265 1266 else: 1267 1268 # Build a descriptive location based on the original 1269 # details, employing the first remaining attribute name. 1270 1271 l = get_attrnames(attrnames) 1272 attrname = l[0] 1273 1274 oa = original_accessor[:-len(l)] 1275 oa = ".".join(oa) 1276 1277 access_location = (location, oa, attrnames, 0) 1278 accessor_location = (location, oa, None, 0) 1279 self.access_index[access_location] = [accessor_location] 1280 1281 self.init_access_details(access_location) 1282 self.init_definition_details(accessor_location) 1283 1284 class_types, instance_types, module_types = ref.get_types() 1285 1286 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, True) 1287 self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True) 1288 1289 original_location = (location, original_name, original_attrnames, 0) 1290 1291 if original_location != access_location: 1292 self.const_accesses[original_location] = access_location 1293 self.const_accesses_rev[access_location] = original_location 1294 1295 # Aliased name definitions. All aliases with usage will have been 1296 # defined, but they may be refined according to referenced accesses. 1297 1298 for accessor_location in self.alias_index.keys(): 1299 self.record_types_for_alias(accessor_location) 1300 1301 # Update accesses employing aliases. 1302 1303 for access_location in alias_accesses: 1304 self.record_types_for_access(access_location, self.access_index[access_location]) 1305 1306 def constrain_types(self, path, class_types, instance_types, module_types): 1307 1308 """ 1309 Using the given 'path' to an object, constrain the given 'class_types', 1310 'instance_types' and 'module_types'. 1311 1312 Return the class, instance, module types plus whether the types are 1313 constrained to a specific kind of type. 1314 """ 1315 1316 ref = self.importer.identify(path) 1317 if ref: 1318 1319 # Constrain usage suggestions using the identified object. 1320 1321 if ref.has_kind("<class>"): 1322 return ( 1323 set(class_types).intersection([ref.get_origin()]), [], [], True 1324 ) 1325 elif ref.has_kind("<module>"): 1326 return ( 1327 [], [], set(module_types).intersection([ref.get_origin()]), True 1328 ) 1329 1330 return class_types, instance_types, module_types, False 1331 1332 def get_target_types(self, location, usage): 1333 1334 """ 1335 Return the class, instance and module types constrained for the name at 1336 the given 'location' exhibiting the given 'usage'. Whether the types 1337 have been constrained using contextual information is also indicated, 1338 plus whether the types have been constrained to a specific kind of type. 1339 """ 1340 1341 unit_path, name, attrnames, version = location 1342 have_assignments = get_assigned_attributes(usage) 1343 1344 # Detect any initialised name for the location. 1345 1346 if name: 1347 ref = self.get_initialised_name(location) 1348 if ref: 1349 (class_types, only_instance_types, module_types, 1350 _function_types, _var_types) = separate_types([ref]) 1351 return class_types, only_instance_types, module_types, True, have_assignments 1352 1353 # Retrieve the recorded types for the usage. 1354 1355 class_types = self.get_class_types_for_usage(usage) 1356 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1357 module_types = self.get_module_types_for_usage(usage) 1358 1359 # Merge usage deductions with observations to obtain reference types 1360 # for names involved with attribute accesses. 1361 1362 if not name: 1363 return class_types, only_instance_types, module_types, False, have_assignments 1364 1365 # Obtain references to known objects. 1366 1367 path = get_name_path(unit_path, name) 1368 1369 class_types, only_instance_types, module_types, constrained_specific = \ 1370 self.constrain_types(path, class_types, only_instance_types, module_types) 1371 1372 if constrained_specific: 1373 return class_types, only_instance_types, module_types, constrained_specific, \ 1374 constrained_specific or have_assignments 1375 1376 # Constrain "self" references. 1377 1378 if name == "self": 1379 1380 # Test for the class of the method in the deduced types. 1381 1382 class_name = self.in_method(unit_path) 1383 1384 if class_name and class_name not in class_types and class_name not in only_instance_types: 1385 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." % 1386 (unit_path, encode_usage(usage), class_name)) 1387 1388 # Constrain the types to the class's hierarchy. 1389 1390 t = self.constrain_self_reference(unit_path, class_types, only_instance_types) 1391 if t: 1392 class_types, only_instance_types, module_types, constrained = t 1393 return class_types, only_instance_types, module_types, constrained, have_assignments 1394 1395 return class_types, only_instance_types, module_types, False, have_assignments 1396 1397 def constrain_self_reference(self, unit_path, class_types, only_instance_types): 1398 1399 """ 1400 Where the name "self" appears in a method, attempt to constrain the 1401 classes involved. 1402 1403 Return the class, instance, module types plus whether the types are 1404 constrained. 1405 """ 1406 1407 class_name = self.in_method(unit_path) 1408 1409 if not class_name: 1410 return None 1411 1412 classes = set([class_name]) 1413 classes.update(self.get_descendants_for_class(class_name)) 1414 1415 # Note that only instances will be expected for these references but 1416 # either classes or instances may provide the attributes. 1417 1418 return ( 1419 set(class_types).intersection(classes), 1420 set(only_instance_types).intersection(classes), 1421 [], True 1422 ) 1423 1424 def in_method(self, path): 1425 1426 "Return whether 'path' refers to a method." 1427 1428 class_name, method_name = path.rsplit(".", 1) 1429 return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name 1430 1431 def init_reference_details(self, location): 1432 1433 "Initialise reference-related details for 'location'." 1434 1435 self.init_definition_details(location) 1436 self.init_access_details(location) 1437 1438 def init_definition_details(self, location): 1439 1440 "Initialise name definition details for 'location'." 1441 1442 self.accessor_class_types[location] = set() 1443 self.accessor_instance_types[location] = set() 1444 self.accessor_module_types[location] = set() 1445 self.provider_class_types[location] = set() 1446 self.provider_instance_types[location] = set() 1447 self.provider_module_types[location] = set() 1448 1449 def init_access_details(self, location): 1450 1451 "Initialise access details at 'location'." 1452 1453 self.referenced_attrs[location] = {} 1454 1455 def record_types_for_access(self, access_location, accessor_locations, alias_accesses=None): 1456 1457 """ 1458 Define types for the 'access_location' associated with the given 1459 'accessor_locations'. 1460 """ 1461 1462 attrname = get_attrname_from_location(access_location) 1463 if not attrname: 1464 return 1465 1466 # Collect all suggested types for the accessors. Accesses may 1467 # require accessors from of a subset of the complete set of types. 1468 1469 class_types = set() 1470 module_types = set() 1471 instance_types = set() 1472 1473 constrained = True 1474 1475 for location in accessor_locations: 1476 1477 # Remember accesses employing aliases. 1478 1479 if alias_accesses is not None and self.alias_index.has_key(location): 1480 alias_accesses.add(access_location) 1481 1482 # Use the type information deduced for names from above. 1483 1484 if self.accessor_class_types.has_key(location): 1485 class_types.update(self.accessor_class_types[location]) 1486 module_types.update(self.accessor_module_types[location]) 1487 instance_types.update(self.accessor_instance_types[location]) 1488 1489 # Where accesses are associated with assignments but where no 1490 # attribute usage observations have caused such an association, 1491 # the attribute name is considered by itself. 1492 1493 else: 1494 self.init_definition_details(location) 1495 self.record_types_for_usage(location, [(attrname, False, False)]) 1496 1497 constrained = location in self.accessor_constrained and constrained 1498 1499 self.init_access_details(access_location) 1500 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained) 1501 1502 def record_types_for_usage(self, accessor_location, usage): 1503 1504 """ 1505 Record types for the given 'accessor_location' according to the given 1506 'usage' observations which may be None to indicate an absence of usage. 1507 """ 1508 1509 (class_types, 1510 instance_types, 1511 module_types, 1512 constrained, 1513 constrained_specific) = self.get_target_types(accessor_location, usage) 1514 1515 invocations = get_invoked_attributes(usage) 1516 1517 self.record_reference_types(accessor_location, class_types, instance_types, 1518 module_types, constrained, constrained_specific, invocations) 1519 1520 def record_types_for_attribute(self, access_location, attrname): 1521 1522 """ 1523 Record types for the 'access_location' employing only the given 1524 'attrname' for type deduction. 1525 """ 1526 1527 (class_types, 1528 only_instance_types, 1529 module_types) = self.get_types_for_attribute(attrname) 1530 1531 self.init_reference_details(access_location) 1532 1533 self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False) 1534 self.record_reference_types(access_location, class_types, only_instance_types, module_types, False) 1535 1536 def get_types_for_attribute(self, attrname): 1537 1538 "Return class, instance-only and module types supporting 'attrname'." 1539 1540 usage = ((attrname, False, False),) 1541 1542 class_types = self.get_class_types_for_usage(usage) 1543 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1544 module_types = self.get_module_types_for_usage(usage) 1545 1546 return class_types, only_instance_types, module_types 1547 1548 def record_types_for_alias(self, accessor_location): 1549 1550 """ 1551 Define types for the 'accessor_location' not having associated usage. 1552 """ 1553 1554 have_access = self.provider_class_types.has_key(accessor_location) 1555 1556 # With an access, attempt to narrow the existing selection of provider 1557 # types. 1558 1559 if have_access: 1560 provider_class_types = self.provider_class_types[accessor_location] 1561 provider_instance_types = self.provider_instance_types[accessor_location] 1562 provider_module_types = self.provider_module_types[accessor_location] 1563 1564 # Find details for any corresponding access. 1565 1566 all_class_types = set() 1567 all_instance_types = set() 1568 all_module_types = set() 1569 1570 for access_location in self.alias_index[accessor_location]: 1571 location, name, attrnames, access_number = access_location 1572 1573 # Alias references an attribute access. 1574 1575 if attrnames: 1576 1577 # Obtain attribute references for the access. 1578 1579 attrs = [attr for _attrtype, object_type, attr in self.referenced_attrs[access_location]] 1580 1581 # Separate the different attribute types. 1582 1583 (class_types, instance_types, module_types, 1584 function_types, var_types) = separate_types(attrs) 1585 1586 # Where non-accessor types are found, do not attempt to refine 1587 # the defined accessor types. 1588 1589 if function_types or var_types: 1590 return 1591 1592 class_types = set(provider_class_types).intersection(class_types) 1593 instance_types = set(provider_instance_types).intersection(instance_types) 1594 module_types = set(provider_module_types).intersection(module_types) 1595 1596 # Alias references a name, not an access. 1597 1598 else: 1599 # Attempt to refine the types using initialised names. 1600 1601 attr = self.get_initialised_name(access_location) 1602 if attr: 1603 (class_types, instance_types, module_types, 1604 _function_types, _var_types) = separate_types([attr]) 1605 1606 # Where no further information is found, do not attempt to 1607 # refine the defined accessor types. 1608 1609 else: 1610 return 1611 1612 all_class_types.update(class_types) 1613 all_instance_types.update(instance_types) 1614 all_module_types.update(module_types) 1615 1616 # Record refined type details for the alias as an accessor. 1617 1618 self.init_definition_details(accessor_location) 1619 self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False) 1620 1621 # Without an access, attempt to identify references for the alias. 1622 1623 else: 1624 refs = set() 1625 1626 for access_location in self.alias_index[accessor_location]: 1627 1628 # Obtain any redefined constant access location. 1629 1630 if self.const_accesses.has_key(access_location): 1631 access_location = self.const_accesses[access_location] 1632 1633 location, name, attrnames, access_number = access_location 1634 1635 # Alias references an attribute access. 1636 1637 if attrnames: 1638 attrs = [attr for attrtype, object_type, attr in self.referenced_attrs[access_location]] 1639 refs.update(attrs) 1640 1641 # Alias references a name, not an access. 1642 1643 else: 1644 attr = self.get_initialised_name(access_location) 1645 attrs = attr and [attr] or [] 1646 if not attrs and self.provider_class_types.has_key(access_location): 1647 class_types = self.provider_class_types[access_location] 1648 instance_types = self.provider_instance_types[access_location] 1649 module_types = self.provider_module_types[access_location] 1650 attrs = combine_types(class_types, instance_types, module_types) 1651 if attrs: 1652 refs.update(attrs) 1653 1654 # Record reference details for the alias separately from accessors. 1655 1656 self.referenced_objects[accessor_location] = refs 1657 1658 def get_initialised_name(self, access_location): 1659 1660 """ 1661 Return references for any initialised names at 'access_location', or 1662 None if no such references exist. 1663 """ 1664 1665 location, name, attrnames, version = access_location 1666 path = get_name_path(location, name) 1667 1668 # Use initialiser information, if available. 1669 1670 refs = self.importer.all_initialised_names.get(path) 1671 if refs and refs.has_key(version): 1672 return refs[version] 1673 else: 1674 return None 1675 1676 def record_reference_types(self, location, class_types, instance_types, 1677 module_types, constrained, constrained_specific=False, invocations=None): 1678 1679 """ 1680 Associate attribute provider types with the given 'location', consisting 1681 of the given 'class_types', 'instance_types' and 'module_types'. 1682 1683 If 'constrained' is indicated, the constrained nature of the accessor is 1684 recorded for the location. 1685 1686 If 'constrained_specific' is indicated using a true value, instance types 1687 will not be added to class types to permit access via instances at the 1688 given location. This is only useful where a specific accessor is known 1689 to be a class. 1690 1691 If 'invocations' is given, the given attribute names indicate those 1692 which are involved in invocations. Such invocations, if involving 1693 functions, will employ those functions as bound methods and will 1694 therefore not support classes as accessors, only instances of such 1695 classes. 1696 1697 Note that the specified types only indicate the provider types for 1698 attributes, whereas the recorded accessor types indicate the possible 1699 types of the actual objects used to access attributes. 1700 """ 1701 1702 # Update the type details for the location. 1703 1704 self.provider_class_types[location].update(class_types) 1705 self.provider_instance_types[location].update(instance_types) 1706 self.provider_module_types[location].update(module_types) 1707 1708 # Class types support classes and instances as accessors. 1709 # Instance-only and module types support only their own kinds as 1710 # accessors. 1711 1712 path, name, version, attrnames = location 1713 1714 if invocations: 1715 class_only_types = self.filter_for_invocations(class_types, invocations) 1716 else: 1717 class_only_types = class_types 1718 1719 # However, the nature of accessors can be further determined. 1720 # Any self variable may only refer to an instance. 1721 1722 if name != "self" or not self.in_method(path): 1723 self.accessor_class_types[location].update(class_only_types) 1724 1725 if not constrained_specific: 1726 self.accessor_instance_types[location].update(class_types) 1727 1728 self.accessor_instance_types[location].update(instance_types) 1729 1730 if name != "self" or not self.in_method(path): 1731 self.accessor_module_types[location].update(module_types) 1732 1733 if constrained: 1734 self.accessor_constrained.add(location) 1735 1736 def filter_for_invocations(self, class_types, attrnames): 1737 1738 """ 1739 From the given 'class_types', identify methods for the given 1740 'attrnames' that are being invoked, returning a filtered collection of 1741 class types. 1742 """ 1743 1744 to_filter = set() 1745 1746 for class_type in class_types: 1747 for attrname in attrnames: 1748 ref = self.importer.get_class_attribute(class_type, attrname) 1749 parent_class = ref and ref.parent() 1750 1751 if ref and ref.has_kind("<function>") and ( 1752 parent_class == class_type or 1753 class_type in self.descendants[parent_class]): 1754 1755 to_filter.add(class_type) 1756 break 1757 1758 return set(class_types).difference(to_filter) 1759 1760 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained): 1761 1762 """ 1763 Identify reference attributes, associating them with the given 1764 'location', identifying the given 'attrname', employing the given 1765 'class_types', 'instance_types' and 'module_types'. 1766 1767 If 'constrained' is indicated, the constrained nature of the access is 1768 recorded for the location. 1769 """ 1770 1771 # Record the referenced objects. 1772 1773 self.referenced_attrs[location] = \ 1774 self._identify_reference_attribute(attrname, class_types, instance_types, module_types) 1775 1776 if constrained: 1777 self.access_constrained.add(location) 1778 1779 def _identify_reference_attribute(self, attrname, class_types, instance_types, module_types): 1780 1781 """ 1782 Identify the reference attribute with the given 'attrname', employing 1783 the given 'class_types', 'instance_types' and 'module_types'. 1784 """ 1785 1786 attrs = set() 1787 1788 # The class types expose class attributes either directly or via 1789 # instances. 1790 1791 for object_type in class_types: 1792 ref = self.importer.get_class_attribute(object_type, attrname) 1793 if ref: 1794 attrs.add(("<class>", object_type, ref)) 1795 1796 # Add any distinct instance attributes that would be provided 1797 # by instances also providing indirect class attribute access. 1798 1799 for ref in self.importer.get_instance_attributes(object_type, attrname): 1800 attrs.add(("<instance>", object_type, ref)) 1801 1802 # The instance-only types expose instance attributes, but although 1803 # classes are excluded as potential accessors (since they do not provide 1804 # the instance attributes), the class types may still provide some 1805 # attributes. 1806 1807 for object_type in instance_types: 1808 instance_attrs = self.importer.get_instance_attributes(object_type, attrname) 1809 1810 if instance_attrs: 1811 for ref in instance_attrs: 1812 attrs.add(("<instance>", object_type, ref)) 1813 else: 1814 ref = self.importer.get_class_attribute(object_type, attrname) 1815 if ref: 1816 attrs.add(("<class>", object_type, ref)) 1817 1818 # Module types expose module attributes for module accessors. 1819 1820 for object_type in module_types: 1821 ref = self.importer.get_module_attribute(object_type, attrname) 1822 if ref: 1823 attrs.add(("<module>", object_type, ref)) 1824 1825 return attrs 1826 1827 class_tests = ( 1828 ("guarded", "specific", "type"), 1829 ("guarded", "common", "type"), 1830 ("test", "specific", "type"), 1831 ("test", "common", "type"), 1832 ) 1833 1834 def get_access_plan(self, location): 1835 1836 """ 1837 Return details of the access at the given 'location'. The details are as 1838 follows: 1839 1840 * the initial accessor (from which accesses will be performed if no 1841 computed static accessor is found) 1842 * details of any test required on the initial accessor 1843 * details of any type employed by the test 1844 * any static accessor (from which accesses will be performed in 1845 preference to the initial accessor) 1846 * attributes needing to be traversed from the base that yield 1847 unambiguous objects 1848 * access modes for each of the unambiguously-traversed attributes 1849 * remaining attributes needing to be tested and traversed 1850 * details of the context 1851 * any test to apply to the context 1852 * the method of obtaining the final attribute 1853 * any static final attribute 1854 * the kinds of objects providing the final attribute 1855 """ 1856 1857 const_access = self.const_accesses_rev.get(location) 1858 1859 path, name, attrnames, version = location 1860 remaining = attrnames.split(".") 1861 attrname = remaining[0] 1862 1863 # Obtain reference and accessor information, retaining also distinct 1864 # provider kind details. 1865 1866 attrs = [] 1867 objtypes = [] 1868 provider_kinds = set() 1869 1870 for attrtype, objtype, attr in self.referenced_attrs[location]: 1871 attrs.append(attr) 1872 objtypes.append(objtype) 1873 provider_kinds.add(attrtype) 1874 1875 # Obtain accessor type and kind information. 1876 1877 accessor_types = self.reference_all_accessor_types[location] 1878 accessor_general_types = self.reference_all_accessor_general_types[location] 1879 accessor_kinds = get_kinds(accessor_general_types) 1880 1881 # Determine any guard or test requirements. 1882 1883 constrained = location in self.access_constrained 1884 test = self.reference_test_types[location] 1885 test_type = self.reference_test_accessor_type.get(location) 1886 1887 # Determine the accessor and provider properties. 1888 1889 class_accessor = "<class>" in accessor_kinds 1890 module_accessor = "<module>" in accessor_kinds 1891 instance_accessor = "<instance>" in accessor_kinds 1892 provided_by_class = "<class>" in provider_kinds 1893 provided_by_instance = "<instance>" in provider_kinds 1894 1895 # Determine how attributes may be accessed relative to the accessor. 1896 1897 object_relative = class_accessor or module_accessor or provided_by_instance 1898 class_relative = instance_accessor and provided_by_class 1899 1900 # Identify the last static attribute for context acquisition. 1901 1902 base = None 1903 dynamic_base = None 1904 1905 # Constant accesses have static accessors. 1906 1907 if const_access: 1908 base = len(objtypes) == 1 and first(objtypes) 1909 1910 # Name-based accesses. 1911 1912 elif name: 1913 ref = self.importer.identify("%s.%s" % (path, name)) 1914 1915 # Constant accessors are static. 1916 1917 if ref and ref.static(): 1918 base = ref.get_origin() 1919 1920 # Usage of previously-generated guard and test details. 1921 1922 elif test[:2] == ("constrained", "specific"): 1923 ref = first(accessor_types) 1924 1925 elif test[:2] == ("constrained", "common"): 1926 ref = first(accessor_general_types) 1927 1928 elif test[:2] == ("guarded", "specific"): 1929 ref = first(accessor_types) 1930 1931 elif test[:2] == ("guarded", "common"): 1932 ref = first(accessor_general_types) 1933 1934 # For attribute-based tests, tentatively identify a dynamic base. 1935 # Such tests allow single or multiple kinds of a type. 1936 1937 elif test[0] == "test" and test[1] in ("common", "specific"): 1938 dynamic_base = test_type 1939 1940 # Static accessors. 1941 1942 if not base and test in self.class_tests: 1943 base = ref and ref.get_origin() or dynamic_base 1944 1945 # Accessors that are not static but whose nature is determined. 1946 1947 elif not base and ref: 1948 dynamic_base = ref.get_origin() 1949 1950 # Determine initial accessor details. 1951 1952 accessor = base or dynamic_base 1953 accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None 1954 provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None 1955 1956 # Traverse remaining attributes. 1957 1958 traversed = [] 1959 traversal_modes = [] 1960 1961 while len(attrs) == 1 and not first(attrs).has_kind("<var>"): 1962 attr = first(attrs) 1963 1964 traversed.append(attrname) 1965 traversal_modes.append(accessor_kind == provider_kind and "object" or "class") 1966 1967 # Consume attribute names providing unambiguous attributes. 1968 1969 del remaining[0] 1970 1971 if not remaining: 1972 break 1973 1974 # Update the last static attribute. 1975 1976 if attr.static(): 1977 base = attr.get_origin() 1978 traversed = [] 1979 traversal_modes = [] 1980 1981 # Get the access details. 1982 1983 attrname = remaining[0] 1984 accessor = attr.get_origin() 1985 accessor_kind = attr.get_kind() 1986 provider_kind = self.importer.get_attribute_provider(attr, attrname) 1987 accessor_kinds = [accessor_kind] 1988 provider_kinds = [provider_kind] 1989 1990 # Get the next attribute. 1991 1992 attrs = self.importer.get_attributes(attr, attrname) 1993 1994 # Where many attributes are suggested, no single attribute identity can 1995 # be loaded. 1996 1997 else: 1998 attr = None 1999 2000 # Determine the method of access. 2001 2002 is_assignment = location in self.reference_assignments or const_access in self.reference_assignments 2003 is_invocation = location in self.reference_invocations or const_access in self.reference_invocations 2004 2005 # Identified attribute that must be accessed via its parent. 2006 2007 if attr and attr.get_name() and is_assignment: 2008 final_method = "static-assign"; origin = attr.get_name() 2009 2010 # Static, identified attribute. 2011 2012 elif attr and attr.static(): 2013 final_method = is_assignment and "static-assign" or \ 2014 is_invocation and "static-invoke" or \ 2015 "static" 2016 origin = attr.final() 2017 2018 # All other methods of access involve traversal. 2019 2020 else: 2021 final_method = is_assignment and "assign" or "access" 2022 origin = None 2023 2024 # First attribute accessed at a known position via the accessor. 2025 2026 # Static bases support object-relative accesses only. 2027 2028 if base: 2029 first_method = "relative-object" 2030 2031 # Dynamic bases support either object- or class-relative accesses. 2032 2033 elif dynamic_base: 2034 first_method = "relative" + (object_relative and "-object" or "") + \ 2035 (class_relative and "-class" or "") 2036 2037 # The fallback case is always run-time testing and access. 2038 2039 else: 2040 first_method = "check" + (object_relative and "-object" or "") + \ 2041 (class_relative and "-class" or "") 2042 2043 # Determine whether an unbound method is being accessed via an instance, 2044 # requiring a context test. 2045 2046 context_test = "ignore" 2047 2048 # Assignments do not employ the context. 2049 2050 if is_assignment: 2051 pass 2052 2053 # Obtain a selection of possible attributes if no unambiguous attribute 2054 # was identified. 2055 2056 elif not attr: 2057 2058 # Use previously-deduced attributes for a simple ambiguous access. 2059 # Otherwise, use the final attribute name to obtain possible 2060 # attributes. 2061 2062 if len(remaining) > 1: 2063 attrname = remaining[-1] 2064 2065 (class_types, 2066 only_instance_types, 2067 module_types) = self.get_types_for_attribute(attrname) 2068 2069 accessor_kinds = set() 2070 provider_kinds = set() 2071 2072 if class_types: 2073 accessor_kinds.add("<class>") 2074 accessor_kinds.add("<instance>") 2075 provider_kinds.add("<class>") 2076 if only_instance_types: 2077 accessor_kinds.add("<instance>") 2078 provider_kinds.add("<instance>") 2079 if module_types: 2080 accessor_kinds.add("<module>") 2081 provider_kinds.add("<module>") 2082 2083 attrs = set() 2084 for type in combine_types(class_types, only_instance_types, module_types): 2085 attrs.update(self.importer.get_attributes(type, attrname)) 2086 2087 always_unbound = True 2088 have_function = False 2089 have_var = False 2090 2091 # Determine whether all attributes are unbound methods and whether 2092 # functions or unidentified attributes occur. 2093 2094 for attr in attrs: 2095 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent() 2096 have_function = have_function or attr.has_kind("<function>") 2097 have_var = have_var or attr.has_kind("<var>") 2098 2099 # Test for class-via-instance accesses. 2100 2101 if accessor_kind == "<instance>" and \ 2102 provider_kind == "<class>": 2103 2104 if always_unbound: 2105 context_test = "replace" 2106 else: 2107 context_test = "test" 2108 2109 # Test for the presence of class-via-instance accesses. 2110 2111 elif "<instance>" in accessor_kinds and \ 2112 "<class>" in provider_kinds and \ 2113 (have_function or have_var): 2114 2115 context_test = "test" 2116 2117 # With an unambiguous attribute, determine whether a test is needed. 2118 2119 elif accessor_kind == "<instance>" and \ 2120 provider_kind == "<class>" and \ 2121 (attr.has_kind("<var>") or 2122 attr.has_kind("<function>") and 2123 attr.name_parent() == attr.parent()): 2124 2125 if attr.has_kind("<var>"): 2126 context_test = "test" 2127 else: 2128 context_test = "replace" 2129 2130 # With an unambiguous attribute with ambiguity in the access method, 2131 # generate a test. 2132 2133 elif "<instance>" in accessor_kinds and \ 2134 "<class>" in provider_kinds and \ 2135 (attr.has_kind("<var>") or 2136 attr.has_kind("<function>") and 2137 attr.name_parent() == attr.parent()): 2138 2139 context_test = "test" 2140 2141 # Determine the nature of the context. 2142 2143 context = context_test == "ignore" and "unset" or \ 2144 len(traversed + remaining) == 1 and \ 2145 (base and "base" or "original-accessor") or \ 2146 "final-accessor" 2147 2148 return name, test, test_type, base, \ 2149 traversed, traversal_modes, remaining, \ 2150 context, context_test, \ 2151 first_method, final_method, \ 2152 origin, accessor_kinds 2153 2154 # vim: tabstop=4 expandtab shiftwidth=4