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: 1864 if ref.has_kind("<class>"): 1865 return ref 1866 elif ref.has_kind("<function>"): 1867 refs = self.importer.all_return_values.get(ref.get_origin()) 1868 if refs and len(refs) == 1: 1869 return first(refs) 1870 1871 return Reference("<var>") 1872 1873 def convert_invocations(self, refs, invocation): 1874 1875 """ 1876 Convert 'refs' to invocation results if 'invocation' is set to a true 1877 value. 1878 """ 1879 1880 return invocation and map(self.convert_invocation, refs) or refs 1881 1882 def convert_invocation(self, ref): 1883 1884 "Convert 'ref' to its invocation result." 1885 1886 if ref: 1887 if ref.has_kind("<class>"): 1888 return ref.instance_of() 1889 elif ref.has_kind("<function>"): 1890 refs = self.importer.all_return_values.get(ref.get_origin()) 1891 if refs and len(refs) == 1: 1892 return first(refs) 1893 1894 return Reference("<var>") 1895 1896 def get_initialised_name(self, access_location): 1897 1898 """ 1899 Return references for any initialised names at 'access_location', or 1900 None if no such references exist. 1901 """ 1902 1903 path, name, attrnames, version = access_location 1904 1905 # Use initialiser information, if available. 1906 1907 refs = self.importer.all_initialised_names.get((path, name)) 1908 if refs and refs.has_key(version): 1909 return refs[version] 1910 else: 1911 return None 1912 1913 def record_reference_types(self, location, class_types, instance_types, 1914 module_types, constrained, constrained_specific=False, invocations=None): 1915 1916 """ 1917 Associate attribute provider types with the given 'location', consisting 1918 of the given 'class_types', 'instance_types' and 'module_types'. 1919 1920 If 'constrained' is indicated, the constrained nature of the accessor is 1921 recorded for the location. 1922 1923 If 'constrained_specific' is indicated using a true value, instance types 1924 will not be added to class types to permit access via instances at the 1925 given location. This is only useful where a specific accessor is known 1926 to be a class. 1927 1928 If 'invocations' is given, the given attribute names indicate those 1929 which are involved in invocations. Such invocations, if involving 1930 functions, will employ those functions as bound methods and will 1931 therefore not support classes as accessors, only instances of such 1932 classes. 1933 1934 Note that the specified types only indicate the provider types for 1935 attributes, whereas the recorded accessor types indicate the possible 1936 types of the actual objects used to access attributes. 1937 """ 1938 1939 # Update the type details for the location. 1940 1941 self.update_provider_types(location, class_types, instance_types, module_types) 1942 1943 # Class types support classes and instances as accessors. 1944 # Instance-only and module types support only their own kinds as 1945 # accessors. 1946 1947 path, name, attrnames, version = location 1948 1949 if invocations: 1950 class_only_types = self.filter_for_invocations(class_types, invocations) 1951 else: 1952 class_only_types = class_types 1953 1954 # However, the nature of accessors can be further determined. 1955 # Any self variable may only refer to an instance. 1956 1957 if name != "self" or not self.in_method(path): 1958 self.accessor_class_types[location].update(class_only_types) 1959 1960 if not constrained_specific: 1961 self.accessor_instance_types[location].update(class_types) 1962 1963 self.accessor_instance_types[location].update(instance_types) 1964 1965 if name != "self" or not self.in_method(path): 1966 self.accessor_module_types[location].update(module_types) 1967 1968 if constrained: 1969 self.accessor_constrained.add(location) 1970 1971 def update_provider_types(self, location, class_types, instance_types, module_types): 1972 1973 """ 1974 Update provider types for the given 'location', adding 'class_types', 1975 'instance_types' and 'module_types' to those already stored. 1976 """ 1977 1978 self.provider_class_types[location].update(class_types) 1979 self.provider_instance_types[location].update(instance_types) 1980 self.provider_module_types[location].update(module_types) 1981 1982 def update_accessor_types(self, location, class_types, instance_types, module_types): 1983 1984 """ 1985 Update accessor types for the given 'location', adding 'class_types', 1986 'instance_types' and 'module_types' to those already stored. 1987 """ 1988 1989 self.accessor_class_types[location].update(class_types) 1990 self.accessor_instance_types[location].update(instance_types) 1991 self.accessor_module_types[location].update(module_types) 1992 1993 def filter_for_invocations(self, class_types, attrnames): 1994 1995 """ 1996 From the given 'class_types', identify methods for the given 1997 'attrnames' that are being invoked, returning a filtered collection of 1998 class types. 1999 2000 This method may be used to remove class types from consideration where 2001 their attributes are methods that are directly invoked: method 2002 invocations must involve instance accessors. 2003 """ 2004 2005 to_filter = set() 2006 2007 for class_type in class_types: 2008 for attrname in attrnames: 2009 2010 # Attempt to obtain a class attribute of the given name. This 2011 # may return an attribute provided by an ancestor class. 2012 2013 ref = self.importer.get_class_attribute(class_type, attrname) 2014 parent_class = ref and ref.parent() 2015 2016 # If such an attribute is a method and would be available on 2017 # the given class, record the class for filtering. 2018 2019 if ref and ref.has_kind("<function>") and ( 2020 parent_class == class_type or 2021 class_type in self.descendants[parent_class]): 2022 2023 to_filter.add(class_type) 2024 break 2025 2026 return set(class_types).difference(to_filter) 2027 2028 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained): 2029 2030 """ 2031 Identify reference attributes, associating them with the given 2032 'location', identifying the given 'attrname', employing the given 2033 'class_types', 'instance_types' and 'module_types'. 2034 2035 If 'constrained' is indicated, the constrained nature of the access is 2036 recorded for the location. 2037 """ 2038 2039 # Record the referenced objects. 2040 2041 self.referenced_attrs[location] = \ 2042 self._identify_reference_attribute(location, attrname, class_types, instance_types, module_types) 2043 2044 if constrained: 2045 self.access_constrained.add(location) 2046 2047 def _identify_reference_attribute(self, location, attrname, class_types, instance_types, module_types): 2048 2049 """ 2050 Identify the reference attribute at the given access 'location', using 2051 the given 'attrname', and employing the given 'class_types', 2052 'instance_types' and 'module_types'. 2053 """ 2054 2055 attrs = set() 2056 2057 # The class types expose class attributes either directly or via 2058 # instances. 2059 2060 for object_type in class_types: 2061 ref = self.importer.get_class_attribute(object_type, attrname) 2062 if ref and self.is_compatible_callable(location, object_type, ref): 2063 attrs.add(("<class>", object_type, ref)) 2064 2065 # Add any distinct instance attributes that would be provided 2066 # by instances also providing indirect class attribute access. 2067 2068 for ref in self.importer.get_instance_attributes(object_type, attrname): 2069 if self.is_compatible_callable(location, object_type, ref): 2070 attrs.add(("<instance>", object_type, ref)) 2071 2072 # The instance-only types expose instance attributes, but although 2073 # classes are excluded as potential accessors (since they do not provide 2074 # the instance attributes), the class types may still provide some 2075 # attributes. 2076 2077 for object_type in instance_types: 2078 instance_attrs = self.importer.get_instance_attributes(object_type, attrname) 2079 2080 if instance_attrs: 2081 for ref in instance_attrs: 2082 if self.is_compatible_callable(location, object_type, ref): 2083 attrs.add(("<instance>", object_type, ref)) 2084 else: 2085 ref = self.importer.get_class_attribute(object_type, attrname) 2086 if ref and self.is_compatible_callable(location, object_type, ref): 2087 attrs.add(("<class>", object_type, ref)) 2088 2089 # Module types expose module attributes for module accessors. 2090 2091 for object_type in module_types: 2092 ref = self.importer.get_module_attribute(object_type, attrname) 2093 if ref and self.is_compatible_callable(location, object_type, ref): 2094 attrs.add(("<module>", object_type, ref)) 2095 2096 return attrs 2097 2098 def is_compatible_callable(self, location, object_type, ref): 2099 2100 """ 2101 Return whether any invocation at 'location' involving an attribute of 2102 'object_type' identified by 'ref' is compatible with any arguments used. 2103 """ 2104 2105 invocation = self.reference_invocations.get(location) 2106 if invocation is None: 2107 return True 2108 2109 objpath = ref.get_origin() 2110 if not objpath: 2111 return True 2112 2113 parameters = self.importer.function_parameters.get(objpath) 2114 if not parameters: 2115 return True 2116 2117 defaults = self.importer.function_defaults.get(objpath) 2118 arguments, keywords = invocation 2119 names = set(parameters) 2120 2121 # Determine whether the specified arguments are 2122 # compatible with the callable signature. 2123 2124 if arguments >= len(parameters) - len(defaults) and \ 2125 arguments <= len(parameters) and \ 2126 names.issuperset(keywords): 2127 2128 return True 2129 else: 2130 init_item(self.reference_invocations_unsuitable, location, set) 2131 self.reference_invocations_unsuitable[location].add(ref) 2132 return False 2133 2134 # Attribute access plan formulation. 2135 2136 class_tests = ( 2137 ("guarded", "specific", "type"), 2138 ("guarded", "common", "type"), 2139 ("test", "specific", "type"), 2140 ("test", "common", "type"), 2141 ) 2142 2143 def get_access_plan(self, location): 2144 2145 """ 2146 Return details of the access at the given 'location'. The details are as 2147 follows: 2148 2149 * the initial accessor (from which accesses will be performed if no 2150 computed static accessor is found) 2151 * details of any test required on the initial accessor 2152 * details of any type employed by the test 2153 * any static accessor (from which accesses will be performed in 2154 preference to the initial accessor) 2155 * attributes needing to be traversed from the base that yield 2156 unambiguous objects 2157 * access modes for each of the unambiguously-traversed attributes 2158 * remaining attributes needing to be tested and traversed 2159 * details of the context 2160 * any test to apply to the context 2161 * the method of obtaining the first attribute 2162 * the method of obtaining the final attribute 2163 * any static final attribute 2164 * the kinds of objects providing the final attribute 2165 """ 2166 2167 const_access = self.const_accesses_rev.get(location) 2168 2169 path, name, attrnames, version = location 2170 remaining = attrnames.split(".") 2171 attrname = remaining[0] 2172 2173 # Obtain reference, provider and provider kind information. 2174 2175 attrs = self.reference_all_attrs[location] 2176 provider_types = self.reference_all_providers[location] 2177 provider_kinds = self.reference_all_provider_kinds[location] 2178 2179 # Obtain accessor type and kind information. 2180 2181 accessor_types = self.reference_all_accessor_types[location] 2182 accessor_general_types = self.reference_all_accessor_general_types[location] 2183 accessor_kinds = get_kinds(accessor_general_types) 2184 2185 # Determine any guard or test requirements. 2186 2187 constrained = location in self.access_constrained 2188 test = self.reference_test_types[location] 2189 test_type = self.reference_test_accessor_type.get(location) 2190 2191 # Determine the accessor and provider properties. 2192 2193 class_accessor = "<class>" in accessor_kinds 2194 module_accessor = "<module>" in accessor_kinds 2195 instance_accessor = "<instance>" in accessor_kinds 2196 provided_by_class = "<class>" in provider_kinds 2197 provided_by_instance = "<instance>" in provider_kinds 2198 2199 # Determine how attributes may be accessed relative to the accessor. 2200 2201 object_relative = class_accessor or module_accessor or provided_by_instance 2202 class_relative = instance_accessor and provided_by_class 2203 2204 # Identify the last static attribute for context acquisition. 2205 2206 base = None 2207 dynamic_base = None 2208 2209 # Constant accesses have static providers. 2210 2211 if const_access: 2212 base = len(provider_types) == 1 and first(provider_types) 2213 2214 # Name-based accesses. 2215 2216 elif name: 2217 ref = self.importer.identify("%s.%s" % (path, name)) 2218 2219 # Constant accessors are static. 2220 2221 if ref and ref.static(): 2222 base = ref.get_origin() 2223 2224 # Usage of previously-generated guard and test details. 2225 2226 elif test[:2] == ("constrained", "specific"): 2227 ref = first(accessor_types) 2228 2229 elif test[:2] == ("constrained", "common"): 2230 ref = first(accessor_general_types) 2231 2232 elif test[:2] == ("guarded", "specific"): 2233 ref = first(accessor_types) 2234 2235 elif test[:2] == ("guarded", "common"): 2236 ref = first(accessor_general_types) 2237 2238 # For attribute-based tests, tentatively identify a dynamic base. 2239 # Such tests allow single or multiple kinds of a type. 2240 2241 elif test[0] == "test" and test[1] in ("common", "specific"): 2242 dynamic_base = test_type 2243 2244 # Static accessors. 2245 2246 if not base and test in self.class_tests: 2247 base = ref and ref.get_origin() or dynamic_base 2248 2249 # Accessors that are not static but whose nature is determined. 2250 2251 elif not base and ref: 2252 dynamic_base = ref.get_origin() 2253 2254 # Determine initial accessor details. 2255 2256 accessor = base or dynamic_base 2257 accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None 2258 provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None 2259 2260 # Traverse remaining attributes. 2261 2262 traversed = [] 2263 traversal_modes = [] 2264 2265 while len(attrs) == 1 and not first(attrs).has_kind("<var>"): 2266 attr = first(attrs) 2267 2268 traversed.append(attrname) 2269 traversal_modes.append(accessor_kind == provider_kind and "object" or "class") 2270 2271 # Consume attribute names providing unambiguous attributes. 2272 2273 del remaining[0] 2274 2275 if not remaining: 2276 break 2277 2278 # Update the last static attribute. 2279 2280 if attr.static(): 2281 base = attr.get_origin() 2282 traversed = [] 2283 traversal_modes = [] 2284 2285 # Get the access details. 2286 2287 attrname = remaining[0] 2288 accessor = attr.get_origin() 2289 accessor_kind = attr.get_kind() 2290 provider_kind = self.importer.get_attribute_provider(attr, attrname) 2291 accessor_kinds = [accessor_kind] 2292 provider_kinds = [provider_kind] 2293 2294 # Get the next attribute. 2295 2296 attrs = self.importer.get_attributes(attr, attrname) 2297 2298 # Where many attributes are suggested, no single attribute identity can 2299 # be loaded. 2300 2301 else: 2302 attr = None 2303 2304 # Determine the method of access. 2305 2306 is_assignment = location in self.reference_assignments or const_access in self.reference_assignments 2307 is_invocation = location in self.reference_invocations or const_access in self.reference_invocations 2308 2309 # Identified attribute that must be accessed via its parent. 2310 2311 if attr and attr.get_name() and is_assignment: 2312 final_method = "static-assign"; origin = attr.get_name() 2313 2314 # Static, identified attribute. 2315 2316 elif attr and attr.static(): 2317 final_method = is_assignment and "static-assign" or \ 2318 is_invocation and "static-invoke" or \ 2319 "static" 2320 origin = attr.final() 2321 2322 # All other methods of access involve traversal. 2323 2324 else: 2325 final_method = is_assignment and "assign" or \ 2326 is_invocation and "access-invoke" or \ 2327 "access" 2328 origin = None 2329 2330 # First attribute accessed at a known position via the accessor. 2331 2332 # Static bases support object-relative accesses only. 2333 2334 if base: 2335 first_method = "relative-object" 2336 2337 # Dynamic bases support either object- or class-relative accesses. 2338 2339 elif dynamic_base: 2340 first_method = "relative" + (object_relative and "-object" or "") + \ 2341 (class_relative and "-class" or "") 2342 2343 # The fallback case is always run-time testing and access. 2344 2345 else: 2346 first_method = "check" + (object_relative and "-object" or "") + \ 2347 (class_relative and "-class" or "") 2348 2349 # Determine whether an unbound method is being accessed via an instance, 2350 # requiring a context test. 2351 2352 context_test = "ignore" 2353 2354 # Assignments do not employ the context. 2355 2356 if is_assignment: 2357 pass 2358 2359 # Obtain a selection of possible attributes if no unambiguous attribute 2360 # was identified. 2361 2362 elif not attr: 2363 2364 # Use previously-deduced attributes for a simple ambiguous access. 2365 # Otherwise, use the final attribute name to obtain possible 2366 # attributes. 2367 2368 if len(remaining) > 1: 2369 attrname = remaining[-1] 2370 2371 (class_types, 2372 only_instance_types, 2373 module_types) = self.get_types_for_attribute(attrname) 2374 2375 accessor_kinds = set() 2376 provider_kinds = set() 2377 2378 if class_types: 2379 accessor_kinds.add("<class>") 2380 accessor_kinds.add("<instance>") 2381 provider_kinds.add("<class>") 2382 if only_instance_types: 2383 accessor_kinds.add("<instance>") 2384 provider_kinds.add("<instance>") 2385 if module_types: 2386 accessor_kinds.add("<module>") 2387 provider_kinds.add("<module>") 2388 2389 attrs = set() 2390 for type in combine_types(class_types, only_instance_types, module_types): 2391 attrs.update(self.importer.get_attributes(type, attrname)) 2392 2393 always_unbound = True 2394 have_function = False 2395 have_var = False 2396 2397 # Determine whether all attributes are unbound methods and whether 2398 # functions or unidentified attributes occur. 2399 2400 for attr in attrs: 2401 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent() 2402 have_function = have_function or attr.has_kind("<function>") 2403 have_var = have_var or attr.has_kind("<var>") 2404 2405 # Test for class-via-instance accesses. 2406 2407 if accessor_kind == "<instance>" and \ 2408 provider_kind == "<class>": 2409 2410 if always_unbound: 2411 context_test = "replace" 2412 else: 2413 context_test = "test" 2414 2415 # Test for the presence of class-via-instance accesses. 2416 2417 elif "<instance>" in accessor_kinds and \ 2418 "<class>" in provider_kinds and \ 2419 (have_function or have_var): 2420 2421 context_test = "test" 2422 2423 # With an unambiguous attribute, determine whether a test is needed. 2424 2425 elif accessor_kind == "<instance>" and \ 2426 provider_kind == "<class>" and \ 2427 (attr.has_kind("<var>") or 2428 attr.has_kind("<function>") and 2429 attr.name_parent() == attr.parent()): 2430 2431 if attr.has_kind("<var>"): 2432 context_test = "test" 2433 else: 2434 context_test = "replace" 2435 2436 # With an unambiguous attribute with ambiguity in the access method, 2437 # generate a test. 2438 2439 elif "<instance>" in accessor_kinds and \ 2440 "<class>" in provider_kinds and \ 2441 (attr.has_kind("<var>") or 2442 attr.has_kind("<function>") and 2443 attr.name_parent() == attr.parent()): 2444 2445 context_test = "test" 2446 2447 # Determine the nature of the context. 2448 2449 context = context_test == "ignore" and "unset" or \ 2450 len(traversed + remaining) == 1 and \ 2451 (base and "base" or "original-accessor") or \ 2452 "final-accessor" 2453 2454 return name, test, test_type, base, \ 2455 traversed, traversal_modes, remaining, \ 2456 context, context_test, \ 2457 first_method, final_method, \ 2458 origin, accessor_kinds 2459 2460 def initialise_access_instructions(self): 2461 2462 "Expand access plans into instruction sequences." 2463 2464 for access_location, access_plan in self.access_plans.items(): 2465 2466 # Obtain the access details. 2467 2468 name, test, test_type, base, \ 2469 traversed, traversal_modes, attrnames, \ 2470 context, context_test, \ 2471 first_method, final_method, \ 2472 origin, accessor_kinds = access_plan 2473 2474 # Emit instructions by appending them to a list. 2475 2476 instructions = [] 2477 emit = instructions.append 2478 2479 # Identify any static original accessor. 2480 2481 if base: 2482 original_accessor = base 2483 2484 # Employ names as contexts unless the context needs testing and 2485 # potentially updating. In such cases, temporary context storage is 2486 # used instead. 2487 2488 elif name and not (context_test == "test" and 2489 final_method in ("access-invoke", "static-invoke")): 2490 original_accessor = "<name>" # refers to the name 2491 2492 # Use a generic placeholder representing the access expression in 2493 # the general case. 2494 2495 else: 2496 original_accessor = "<expr>" 2497 2498 # Prepare for any first attribute access. 2499 2500 if traversed: 2501 attrname = traversed[0] 2502 del traversed[0] 2503 elif attrnames: 2504 attrname = attrnames[0] 2505 del attrnames[0] 2506 2507 # Perform the first access explicitly if at least one operation 2508 # requires it. 2509 2510 access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames 2511 2512 # Determine whether the first access involves assignment. 2513 2514 assigning = not traversed and not attrnames and final_method == "assign" 2515 set_accessor = assigning and "<set_target_accessor>" or "<set_accessor>" 2516 stored_accessor = assigning and "<target_accessor>" or "<accessor>" 2517 2518 # Set the context if already available. 2519 2520 context_var = None 2521 2522 if context == "base": 2523 accessor = context_var = (base,) 2524 elif context == "original-accessor": 2525 2526 # Prevent re-evaluation of any dynamic expression by storing it. 2527 2528 if original_accessor == "<expr>": 2529 if final_method in ("access-invoke", "static-invoke"): 2530 emit(("<set_context>", original_accessor)) 2531 accessor = context_var = ("<context>",) 2532 else: 2533 emit((set_accessor, original_accessor)) 2534 accessor = context_var = (stored_accessor,) 2535 else: 2536 accessor = context_var = (original_accessor,) 2537 2538 # Assigning does not set the context. 2539 2540 elif context in ("final-accessor", "unset") and access_first_attribute: 2541 2542 # Prevent re-evaluation of any dynamic expression by storing it. 2543 2544 if original_accessor == "<expr>": 2545 emit((set_accessor, original_accessor)) 2546 accessor = (stored_accessor,) 2547 else: 2548 accessor = (original_accessor,) 2549 2550 # Apply any test. 2551 2552 if test[0] == "test": 2553 accessor = ("__%s_%s_%s" % test, accessor, test_type) 2554 2555 # Perform the first or final access. 2556 # The access only needs performing if the resulting accessor is used. 2557 2558 remaining = len(traversed + attrnames) 2559 2560 if access_first_attribute: 2561 2562 if first_method == "relative-class": 2563 if assigning: 2564 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2565 else: 2566 accessor = ("__load_via_class", accessor, attrname) 2567 2568 elif first_method == "relative-object": 2569 if assigning: 2570 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2571 else: 2572 accessor = ("__load_via_object", accessor, attrname) 2573 2574 elif first_method == "relative-object-class": 2575 if assigning: 2576 emit(("__get_class_and_store", accessor, attrname, "<assexpr>")) 2577 else: 2578 accessor = ("__get_class_and_load", accessor, attrname) 2579 2580 elif first_method == "check-class": 2581 if assigning: 2582 emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>")) 2583 else: 2584 accessor = ("__check_and_load_via_class", accessor, attrname) 2585 2586 elif first_method == "check-object": 2587 if assigning: 2588 emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>")) 2589 else: 2590 accessor = ("__check_and_load_via_object", accessor, attrname) 2591 2592 elif first_method == "check-object-class": 2593 if assigning: 2594 emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) 2595 else: 2596 accessor = ("__check_and_load_via_any", accessor, attrname) 2597 2598 # Traverse attributes using the accessor. 2599 2600 if traversed: 2601 for attrname, traversal_mode in zip(traversed, traversal_modes): 2602 assigning = remaining == 1 and final_method == "assign" 2603 2604 # Set the context, if appropriate. 2605 2606 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2607 2608 # Invoked attributes employ a separate context accessed 2609 # during invocation. 2610 2611 if final_method in ("access-invoke", "static-invoke"): 2612 emit(("<set_context>", accessor)) 2613 accessor = context_var = "<context>" 2614 2615 # A private context within the access is otherwise 2616 # retained. 2617 2618 else: 2619 emit(("<set_private_context>", accessor)) 2620 accessor = context_var = "<private_context>" 2621 2622 # Perform the access only if not achieved directly. 2623 2624 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2625 2626 if traversal_mode == "class": 2627 if assigning: 2628 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2629 else: 2630 accessor = ("__load_via_class", accessor, attrname) 2631 else: 2632 if assigning: 2633 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2634 else: 2635 accessor = ("__load_via_object", accessor, attrname) 2636 2637 remaining -= 1 2638 2639 if attrnames: 2640 for attrname in attrnames: 2641 assigning = remaining == 1 and final_method == "assign" 2642 2643 # Set the context, if appropriate. 2644 2645 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2646 2647 # Invoked attributes employ a separate context accessed 2648 # during invocation. 2649 2650 if final_method in ("access-invoke", "static-invoke"): 2651 emit(("<set_context>", accessor)) 2652 accessor = context_var = "<context>" 2653 2654 # A private context within the access is otherwise 2655 # retained. 2656 2657 else: 2658 emit(("<set_private_context>", accessor)) 2659 accessor = context_var = "<private_context>" 2660 2661 # Perform the access only if not achieved directly. 2662 2663 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2664 2665 # Constrain instructions involving certain special 2666 # attribute names. 2667 2668 to_search = attrname == "__data__" and "object" or "any" 2669 2670 if assigning: 2671 emit(("__check_and_store_via_%s" % to_search, accessor, attrname, "<assexpr>")) 2672 else: 2673 accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname) 2674 2675 remaining -= 1 2676 2677 # Define or emit the means of accessing the actual target. 2678 2679 # Assignments to known attributes. 2680 2681 if final_method == "static-assign": 2682 parent, attrname = origin.rsplit(".", 1) 2683 emit(("__store_via_object", parent, attrname, "<assexpr>")) 2684 2685 # Invoked attributes employ a separate context. 2686 2687 elif final_method in ("static", "static-invoke"): 2688 accessor = ("__load_static_ignore", origin) 2689 2690 # Wrap accesses in context operations. 2691 2692 if context_test == "test": 2693 2694 # Test and combine the context with static attribute details. 2695 2696 if final_method == "static": 2697 emit(("__load_static_test", context_var, origin)) 2698 2699 # Test the context, storing it separately if required for the 2700 # immediately invoked static attribute. 2701 2702 elif final_method == "static-invoke": 2703 emit(("<test_context_static>", context_var, origin)) 2704 2705 # Test the context, storing it separately if required for an 2706 # immediately invoked attribute. 2707 2708 elif final_method == "access-invoke": 2709 emit(("<test_context_revert>", context_var, accessor)) 2710 2711 # Test the context and update the attribute details if 2712 # appropriate. 2713 2714 else: 2715 emit(("__test_context", context_var, accessor)) 2716 2717 elif context_test == "replace": 2718 2719 # Produce an object with updated context. 2720 2721 if final_method == "static": 2722 emit(("__load_static_replace", context_var, origin)) 2723 2724 # Omit the context update operation where the target is static 2725 # and the context is recorded separately. 2726 2727 elif final_method == "static-invoke": 2728 pass 2729 2730 # If a separate context is used for an immediate invocation, 2731 # produce the attribute details unchanged. 2732 2733 elif final_method == "access-invoke": 2734 emit(accessor) 2735 2736 # Update the context in the attribute details. 2737 2738 else: 2739 emit(("__update_context", context_var, accessor)) 2740 2741 # Omit the accessor for assignments and for invocations of static 2742 # targets. 2743 2744 elif final_method not in ("assign", "static-assign", "static-invoke"): 2745 emit(accessor) 2746 2747 # Produce an advisory instruction regarding the context. 2748 2749 if context_var: 2750 emit(("<context_identity>", context_var)) 2751 2752 self.access_instructions[access_location] = instructions 2753 self.accessor_kinds[access_location] = accessor_kinds 2754 2755 # vim: tabstop=4 expandtab shiftwidth=4