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