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