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