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