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 (path, name), all_aliases in self.importer.all_aliased_names.items(): 947 948 # For each version of the name, obtain the access location. 949 950 for version, (original_path, original_name, attrnames, access_number) in all_aliases.items(): 951 accessor_location = (path, name, None, version) 952 access_location = (original_path, original_name, attrnames, access_number) 953 init_item(self.alias_index, accessor_location, list) 954 self.alias_index[accessor_location].append(access_location) 955 956 # Get aliases in terms of non-aliases and accesses. 957 958 for accessor_location, access_locations in self.alias_index.items(): 959 self.update_aliases(accessor_location, access_locations) 960 961 def update_aliases(self, accessor_location, access_locations, visited=None): 962 963 """ 964 Update the given 'accessor_location' defining an alias, update 965 'access_locations' to refer to non-aliases, following name references 966 via the access index. 967 968 If 'visited' is specified, it contains a set of accessor locations (and 969 thus keys to the alias index) that are currently being defined. 970 """ 971 972 if visited is None: 973 visited = set() 974 975 updated_locations = set() 976 977 for access_location in access_locations: 978 (path, original_name, attrnames, access_number) = access_location 979 980 # Where an alias refers to a name access, obtain the original name 981 # version details. 982 983 if attrnames is None: 984 985 # For each name version, attempt to determine any accesses that 986 # initialise the name. 987 988 for name_accessor_location in self.access_index[access_location]: 989 990 # Already-visited aliases do not contribute details. 991 992 if name_accessor_location in visited: 993 continue 994 995 visited.add(name_accessor_location) 996 997 name_access_locations = self.alias_index.get(name_accessor_location) 998 if name_access_locations: 999 updated_locations.update(self.update_aliases(name_accessor_location, name_access_locations, visited)) 1000 else: 1001 updated_locations.add(name_accessor_location) 1002 1003 # Otherwise, record the access details. 1004 1005 else: 1006 updated_locations.add(access_location) 1007 1008 self.alias_index[accessor_location] = updated_locations 1009 return updated_locations 1010 1011 # Attribute mutation for types. 1012 1013 def modify_mutated_attributes(self): 1014 1015 "Identify known, mutated attributes and change their state." 1016 1017 # Usage-based accesses. 1018 1019 for usage, all_attrnames in self.assigned_attrs.items(): 1020 if not usage: 1021 continue 1022 1023 for path, name, attrname in all_attrnames: 1024 class_types = self.get_class_types_for_usage(usage) 1025 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1026 module_types = self.get_module_types_for_usage(usage) 1027 1028 # Detect self usage within methods in order to narrow the scope 1029 # of the mutation. 1030 1031 t = name == "self" and self.constrain_self_reference(path, class_types, only_instance_types) 1032 if t: 1033 class_types, only_instance_types, module_types, constrained = t 1034 objects = set(class_types).union(only_instance_types).union(module_types) 1035 1036 self.mutate_attribute(objects, attrname) 1037 1038 def mutate_attribute(self, objects, attrname): 1039 1040 "Mutate static 'objects' with the given 'attrname'." 1041 1042 for name in objects: 1043 attr = "%s.%s" % (name, attrname) 1044 value = self.importer.get_object(attr) 1045 1046 # If the value is None, the attribute is 1047 # inherited and need not be set explicitly on 1048 # the class concerned. 1049 1050 if value: 1051 self.modified_attributes[attr] = value 1052 self.importer.set_object(attr, value.as_var()) 1053 1054 # Simplification of types. 1055 1056 def get_most_general_types(self, types): 1057 1058 "Return the most general types for the given 'types'." 1059 1060 module_types = set() 1061 class_types = set() 1062 1063 for type in types: 1064 ref = self.importer.identify(type) 1065 if ref.has_kind("<module>"): 1066 module_types.add(type) 1067 else: 1068 class_types.add(type) 1069 1070 types = set(self.get_most_general_module_types(module_types)) 1071 types.update(self.get_most_general_class_types(class_types)) 1072 return types 1073 1074 def get_most_general_class_types(self, class_types): 1075 1076 "Return the most general types for the given 'class_types'." 1077 1078 class_types = set(class_types) 1079 to_remove = set() 1080 1081 for class_type in class_types: 1082 for base in self.importer.classes[class_type]: 1083 base = base.get_origin() 1084 descendants = self.descendants[base] 1085 if base in class_types and descendants.issubset(class_types): 1086 to_remove.update(descendants) 1087 1088 class_types.difference_update(to_remove) 1089 return class_types 1090 1091 def get_most_general_module_types(self, module_types): 1092 1093 "Return the most general type for the given 'module_types'." 1094 1095 # Where all modules are provided, an object would provide the same 1096 # attributes. 1097 1098 if len(module_types) == len(self.importer.modules): 1099 return [self.root_class_type] 1100 else: 1101 return module_types 1102 1103 # More efficient usage-to-type indexing and retrieval. 1104 1105 def init_attr_type_indexes(self): 1106 1107 "Identify the types that can support each attribute name." 1108 1109 self._init_attr_type_index(self.attr_class_types, self.importer.all_class_attrs) 1110 self._init_attr_type_index(self.attr_instance_types, self.importer.all_instance_attrs, True) 1111 self._init_attr_type_index(self.attr_instance_types, self.importer.all_combined_attrs, False) 1112 self._init_attr_type_index(self.attr_module_types, self.importer.all_module_attrs) 1113 1114 def _init_attr_type_index(self, attr_types, attrs, assignment=None): 1115 1116 """ 1117 Initialise the 'attr_types' attribute-to-types mapping using the given 1118 'attrs' type-to-attributes mapping. 1119 """ 1120 1121 for name, attrnames in attrs.items(): 1122 for attrname in attrnames: 1123 1124 # Permit general access for certain kinds of object. 1125 1126 if assignment is None: 1127 init_item(attr_types, (attrname, False), set) 1128 init_item(attr_types, (attrname, True), set) 1129 attr_types[(attrname, False)].add(name) 1130 attr_types[(attrname, True)].add(name) 1131 1132 # Restrict attribute assignment for instances. 1133 1134 else: 1135 init_item(attr_types, (attrname, assignment), set) 1136 attr_types[(attrname, assignment)].add(name) 1137 1138 def get_class_types_for_usage(self, usage): 1139 1140 "Return names of classes supporting the given 'usage'." 1141 1142 return self._get_types_for_usage(usage, self.attr_class_types, self.importer.all_class_attrs) 1143 1144 def get_instance_types_for_usage(self, usage): 1145 1146 """ 1147 Return names of classes whose instances support the given 'usage' 1148 (as either class or instance attributes). 1149 """ 1150 1151 return self._get_types_for_usage(usage, self.attr_instance_types, self.importer.all_combined_attrs) 1152 1153 def get_module_types_for_usage(self, usage): 1154 1155 "Return names of modules supporting the given 'usage'." 1156 1157 return self._get_types_for_usage(usage, self.attr_module_types, self.importer.all_module_attrs) 1158 1159 def _get_types_for_usage(self, usage, attr_types, attrs): 1160 1161 """ 1162 For the given 'usage' representing attribute usage, return types 1163 recorded in the 'attr_types' attribute-to-types mapping that support 1164 such usage, with the given 'attrs' type-to-attributes mapping used to 1165 quickly assess whether a type supports all of the stated attributes. 1166 """ 1167 1168 # Where no attributes are used, any type would be acceptable. 1169 1170 if not usage: 1171 return attrs.keys() 1172 1173 keys = [] 1174 for attrname, invocation, assignment in usage: 1175 keys.append((attrname, assignment)) 1176 1177 # Obtain types supporting the first (attribute name, assignment) key... 1178 1179 types = set(attr_types.get(keys[0]) or []) 1180 1181 for key in keys[1:]: 1182 1183 # Record types that support all of the other attributes as well. 1184 1185 types.intersection_update(attr_types.get(key) or []) 1186 1187 return types 1188 1189 def init_combined_attribute_index(self): 1190 1191 "Initialise a combined index for the detection of invalid attributes." 1192 1193 self.all_attrnames = set() 1194 for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs): 1195 for name, attrnames in attrs.items(): 1196 self.all_attrnames.update(attrnames) 1197 1198 # Reference identification. 1199 1200 def identify_references(self): 1201 1202 "Identify references using usage and name reference information." 1203 1204 # Names with associated attribute usage. 1205 1206 for location, usages in self.location_index.items(): 1207 1208 # Obtain attribute usage associated with a name, deducing the nature 1209 # of the name. Obtain types only for branches involving attribute 1210 # usage. (In the absence of usage, any type could be involved, but 1211 # then no accesses exist to require knowledge of the type.) 1212 1213 have_usage = False 1214 have_no_usage_branch = False 1215 1216 for usage in usages: 1217 if not usage: 1218 have_no_usage_branch = True 1219 continue 1220 elif not have_usage: 1221 self.init_definition_details(location) 1222 have_usage = True 1223 self.record_types_for_usage(location, usage) 1224 1225 # Where some usage occurs, but where branches without usage also 1226 # occur, record the types for those branches anyway. 1227 1228 if have_usage and have_no_usage_branch: 1229 self.init_definition_details(location) 1230 self.record_types_for_usage(location, None) 1231 1232 # Specific name-based attribute accesses. 1233 1234 alias_accesses = set() 1235 1236 for access_location, accessor_locations in self.access_index.items(): 1237 self.record_types_for_access(access_location, accessor_locations, alias_accesses) 1238 1239 # Anonymous references with attribute chains. 1240 1241 for location, accesses in self.importer.all_attr_accesses.items(): 1242 1243 # Get distinct attribute names. 1244 1245 all_attrnames = set() 1246 1247 for attrnames in accesses: 1248 all_attrnames.update(get_attrnames(attrnames)) 1249 1250 # Get attribute and accessor details for each attribute name. 1251 1252 for attrname in all_attrnames: 1253 access_location = (location, None, attrname, 0) 1254 self.record_types_for_attribute(access_location, attrname) 1255 1256 # References via constant/identified objects. 1257 1258 for location, name_accesses in self.importer.all_const_accesses.items(): 1259 1260 # A mapping from the original name and attributes to resolved access 1261 # details. 1262 1263 for original_access, access in name_accesses.items(): 1264 original_name, original_attrnames = original_access 1265 objpath, ref, attrnames = access 1266 1267 # Build an accessor combining the name and attribute names used. 1268 1269 original_accessor = tuple([original_name] + original_attrnames.split(".")) 1270 1271 # Direct accesses to attributes. 1272 1273 if not attrnames: 1274 1275 # Build a descriptive location based on the original 1276 # details, exposing the final attribute name. 1277 1278 oa, attrname = original_accessor[:-1], original_accessor[-1] 1279 oa = ".".join(oa) 1280 1281 access_location = (location, oa, attrname, 0) 1282 accessor_location = (location, oa, None, 0) 1283 self.access_index[access_location] = [accessor_location] 1284 1285 self.init_access_details(access_location) 1286 self.init_definition_details(accessor_location) 1287 1288 # Obtain a reference for the accessor in order to properly 1289 # determine its type. 1290 1291 if ref.get_kind() != "<instance>": 1292 objpath = ref.get_origin() 1293 1294 objpath = objpath.rsplit(".", 1)[0] 1295 1296 # Where the object name conflicts with the module 1297 # providing it, obtain the module details. 1298 1299 if objpath in self.importer.modules: 1300 accessor = Reference("<module>", objpath) 1301 else: 1302 accessor = self.importer.get_object(objpath) 1303 1304 self.referenced_attrs[access_location] = [(accessor.get_kind(), accessor.get_origin(), ref)] 1305 self.access_constrained.add(access_location) 1306 1307 class_types, instance_types, module_types = accessor.get_types() 1308 self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True) 1309 1310 else: 1311 1312 # Build a descriptive location based on the original 1313 # details, employing the first remaining attribute name. 1314 1315 l = get_attrnames(attrnames) 1316 attrname = l[0] 1317 1318 oa = original_accessor[:-len(l)] 1319 oa = ".".join(oa) 1320 1321 access_location = (location, oa, attrnames, 0) 1322 accessor_location = (location, oa, None, 0) 1323 self.access_index[access_location] = [accessor_location] 1324 1325 self.init_access_details(access_location) 1326 self.init_definition_details(accessor_location) 1327 1328 class_types, instance_types, module_types = ref.get_types() 1329 1330 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, True) 1331 self.record_reference_types(accessor_location, class_types, instance_types, module_types, True, True) 1332 1333 # Define mappings between the original and access locations 1334 # so that translation can work from the source details. 1335 1336 original_location = (location, original_name, original_attrnames, 0) 1337 1338 if original_location != access_location: 1339 self.const_accesses[original_location] = access_location 1340 self.const_accesses_rev[access_location] = original_location 1341 1342 # Aliased name definitions. All aliases with usage will have been 1343 # defined, but they may be refined according to referenced accesses. 1344 1345 for accessor_location in self.alias_index.keys(): 1346 self.record_types_for_alias(accessor_location) 1347 1348 # Update accesses employing aliases. 1349 1350 for access_location in alias_accesses: 1351 self.record_types_for_access(access_location, self.access_index[access_location]) 1352 1353 def constrain_types(self, path, class_types, instance_types, module_types): 1354 1355 """ 1356 Using the given 'path' to an object, constrain the given 'class_types', 1357 'instance_types' and 'module_types'. 1358 1359 Return the class, instance, module types plus whether the types are 1360 constrained to a specific kind of type. 1361 """ 1362 1363 ref = self.importer.identify(path) 1364 if ref: 1365 1366 # Constrain usage suggestions using the identified object. 1367 1368 if ref.has_kind("<class>"): 1369 return ( 1370 set(class_types).intersection([ref.get_origin()]), [], [], True 1371 ) 1372 elif ref.has_kind("<module>"): 1373 return ( 1374 [], [], set(module_types).intersection([ref.get_origin()]), True 1375 ) 1376 1377 return class_types, instance_types, module_types, False 1378 1379 def get_target_types(self, location, usage): 1380 1381 """ 1382 Return the class, instance and module types constrained for the name at 1383 the given 'location' exhibiting the given 'usage'. Whether the types 1384 have been constrained using contextual information is also indicated, 1385 plus whether the types have been constrained to a specific kind of type. 1386 """ 1387 1388 unit_path, name, attrnames, version = location 1389 have_assignments = get_assigned_attributes(usage) 1390 1391 # Detect any initialised name for the location. 1392 1393 if name: 1394 ref = self.get_initialised_name(location) 1395 if ref: 1396 (class_types, only_instance_types, module_types, 1397 _function_types, _var_types) = separate_types([ref]) 1398 return class_types, only_instance_types, module_types, True, have_assignments 1399 1400 # Retrieve the recorded types for the usage. 1401 1402 class_types = self.get_class_types_for_usage(usage) 1403 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1404 module_types = self.get_module_types_for_usage(usage) 1405 1406 # Merge usage deductions with observations to obtain reference types 1407 # for names involved with attribute accesses. 1408 1409 if not name: 1410 return class_types, only_instance_types, module_types, False, have_assignments 1411 1412 # Obtain references to known objects. 1413 1414 path = get_name_path(unit_path, name) 1415 1416 class_types, only_instance_types, module_types, constrained_specific = \ 1417 self.constrain_types(path, class_types, only_instance_types, module_types) 1418 1419 if constrained_specific: 1420 return class_types, only_instance_types, module_types, constrained_specific, \ 1421 constrained_specific or have_assignments 1422 1423 # Constrain "self" references. 1424 1425 if name == "self": 1426 1427 # Test for the class of the method in the deduced types. 1428 1429 class_name = self.in_method(unit_path) 1430 1431 if class_name and class_name not in class_types and class_name not in only_instance_types: 1432 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." % 1433 (unit_path, encode_usage(usage), class_name)) 1434 1435 # Constrain the types to the class's hierarchy. 1436 1437 t = self.constrain_self_reference(unit_path, class_types, only_instance_types) 1438 if t: 1439 class_types, only_instance_types, module_types, constrained = t 1440 return class_types, only_instance_types, module_types, constrained, have_assignments 1441 1442 return class_types, only_instance_types, module_types, False, have_assignments 1443 1444 def constrain_self_reference(self, unit_path, class_types, only_instance_types): 1445 1446 """ 1447 Where the name "self" appears in a method, attempt to constrain the 1448 classes involved. 1449 1450 Return the class, instance, module types plus whether the types are 1451 constrained. 1452 """ 1453 1454 class_name = self.in_method(unit_path) 1455 1456 if not class_name: 1457 return None 1458 1459 classes = set([class_name]) 1460 classes.update(self.get_descendants_for_class(class_name)) 1461 1462 # Note that only instances will be expected for these references but 1463 # either classes or instances may provide the attributes. 1464 1465 return ( 1466 set(class_types).intersection(classes), 1467 set(only_instance_types).intersection(classes), 1468 [], True 1469 ) 1470 1471 def in_method(self, path): 1472 1473 "Return whether 'path' refers to a method." 1474 1475 class_name, method_name = path.rsplit(".", 1) 1476 return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name 1477 1478 def init_reference_details(self, location): 1479 1480 "Initialise reference-related details for 'location'." 1481 1482 self.init_definition_details(location) 1483 self.init_access_details(location) 1484 1485 def init_definition_details(self, location): 1486 1487 "Initialise name definition details for 'location'." 1488 1489 self.accessor_class_types[location] = set() 1490 self.accessor_instance_types[location] = set() 1491 self.accessor_module_types[location] = set() 1492 self.provider_class_types[location] = set() 1493 self.provider_instance_types[location] = set() 1494 self.provider_module_types[location] = set() 1495 1496 def init_access_details(self, location): 1497 1498 "Initialise access details at 'location'." 1499 1500 self.referenced_attrs[location] = {} 1501 1502 def record_types_for_access(self, access_location, accessor_locations, alias_accesses=None): 1503 1504 """ 1505 Define types for the 'access_location' associated with the given 1506 'accessor_locations'. 1507 """ 1508 1509 attrname = get_attrname_from_location(access_location) 1510 if not attrname: 1511 return 1512 1513 # Collect all suggested types for the accessors. Accesses may 1514 # require accessors from of a subset of the complete set of types. 1515 1516 class_types = set() 1517 module_types = set() 1518 instance_types = set() 1519 1520 constrained = True 1521 1522 for location in accessor_locations: 1523 1524 # Remember accesses employing aliases. 1525 1526 if alias_accesses is not None and self.alias_index.has_key(location): 1527 alias_accesses.add(access_location) 1528 1529 # Use the type information deduced for names from above. 1530 1531 if self.accessor_class_types.has_key(location): 1532 class_types.update(self.accessor_class_types[location]) 1533 module_types.update(self.accessor_module_types[location]) 1534 instance_types.update(self.accessor_instance_types[location]) 1535 1536 # Where accesses are associated with assignments but where no 1537 # attribute usage observations have caused such an association, 1538 # the attribute name is considered by itself. 1539 1540 else: 1541 self.init_definition_details(location) 1542 self.record_types_for_usage(location, [(attrname, False, False)]) 1543 1544 constrained = location in self.accessor_constrained and constrained 1545 1546 self.init_access_details(access_location) 1547 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained) 1548 1549 def record_types_for_usage(self, accessor_location, usage): 1550 1551 """ 1552 Record types for the given 'accessor_location' according to the given 1553 'usage' observations which may be None to indicate an absence of usage. 1554 """ 1555 1556 (class_types, 1557 instance_types, 1558 module_types, 1559 constrained, 1560 constrained_specific) = self.get_target_types(accessor_location, usage) 1561 1562 invocations = get_invoked_attributes(usage) 1563 1564 self.record_reference_types(accessor_location, class_types, instance_types, 1565 module_types, constrained, constrained_specific, invocations) 1566 1567 def record_types_for_attribute(self, access_location, attrname): 1568 1569 """ 1570 Record types for the 'access_location' employing only the given 1571 'attrname' for type deduction. 1572 """ 1573 1574 (class_types, 1575 only_instance_types, 1576 module_types) = self.get_types_for_attribute(attrname) 1577 1578 self.init_reference_details(access_location) 1579 1580 self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False) 1581 self.record_reference_types(access_location, class_types, only_instance_types, module_types, False) 1582 1583 def get_types_for_attribute(self, attrname): 1584 1585 "Return class, instance-only and module types supporting 'attrname'." 1586 1587 usage = ((attrname, False, False),) 1588 1589 class_types = self.get_class_types_for_usage(usage) 1590 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1591 module_types = self.get_module_types_for_usage(usage) 1592 1593 return class_types, only_instance_types, module_types 1594 1595 def record_types_for_alias(self, accessor_location): 1596 1597 """ 1598 Define types for the 'accessor_location' not having associated usage. 1599 """ 1600 1601 have_access = self.provider_class_types.has_key(accessor_location) 1602 1603 # With an access, attempt to narrow the existing selection of provider 1604 # types. 1605 1606 if have_access: 1607 provider_class_types = self.provider_class_types[accessor_location] 1608 provider_instance_types = self.provider_instance_types[accessor_location] 1609 provider_module_types = self.provider_module_types[accessor_location] 1610 1611 # Find details for any corresponding access. 1612 1613 all_class_types = set() 1614 all_instance_types = set() 1615 all_module_types = set() 1616 1617 for access_location in self.alias_index[accessor_location]: 1618 location, name, attrnames, access_number = access_location 1619 1620 # Alias references an attribute access. 1621 1622 if attrnames: 1623 1624 # Obtain attribute references for the access. 1625 1626 attrs = [] 1627 for _attrtype, object_type, attr in self.referenced_attrs[access_location]: 1628 attrs.append(attr) 1629 1630 # Separate the different attribute types. 1631 1632 (class_types, instance_types, module_types, 1633 function_types, var_types) = separate_types(attrs) 1634 1635 # Where non-accessor types are found, do not attempt to refine 1636 # the defined accessor types. 1637 1638 if function_types or var_types: 1639 return 1640 1641 class_types = set(provider_class_types).intersection(class_types) 1642 instance_types = set(provider_instance_types).intersection(instance_types) 1643 module_types = set(provider_module_types).intersection(module_types) 1644 1645 # Alias references a name, not an access. 1646 1647 else: 1648 # Attempt to refine the types using initialised names. 1649 1650 attr = self.get_initialised_name(access_location) 1651 if attr: 1652 (class_types, instance_types, module_types, 1653 _function_types, _var_types) = separate_types([attr]) 1654 1655 # Where no further information is found, do not attempt to 1656 # refine the defined accessor types. 1657 1658 else: 1659 return 1660 1661 all_class_types.update(class_types) 1662 all_instance_types.update(instance_types) 1663 all_module_types.update(module_types) 1664 1665 # Record refined type details for the alias as an accessor. 1666 1667 self.init_definition_details(accessor_location) 1668 self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False) 1669 1670 # Without an access, attempt to identify references for the alias. 1671 1672 else: 1673 refs = set() 1674 1675 for access_location in self.alias_index[accessor_location]: 1676 1677 # Obtain any redefined constant access location. 1678 1679 if self.const_accesses.has_key(access_location): 1680 access_location = self.const_accesses[access_location] 1681 1682 location, name, attrnames, access_number = access_location 1683 1684 # Alias references an attribute access. 1685 1686 if attrnames: 1687 attrs = [] 1688 for attrtype, object_type, attr in self.referenced_attrs[access_location]: 1689 attrs.append(attr) 1690 refs.update(attrs) 1691 1692 # Alias references a name, not an access. 1693 1694 else: 1695 attr = self.get_initialised_name(access_location) 1696 attrs = attr and [attr] or [] 1697 if not attrs and self.provider_class_types.has_key(access_location): 1698 class_types = self.provider_class_types[access_location] 1699 instance_types = self.provider_instance_types[access_location] 1700 module_types = self.provider_module_types[access_location] 1701 attrs = combine_types(class_types, instance_types, module_types) 1702 if attrs: 1703 refs.update(attrs) 1704 1705 # Record reference details for the alias separately from accessors. 1706 1707 self.referenced_objects[accessor_location] = refs 1708 1709 def get_initialised_name(self, access_location): 1710 1711 """ 1712 Return references for any initialised names at 'access_location', or 1713 None if no such references exist. 1714 """ 1715 1716 path, name, attrnames, version = access_location 1717 1718 # Use initialiser information, if available. 1719 1720 refs = self.importer.all_initialised_names.get((path, name)) 1721 if refs and refs.has_key(version): 1722 return refs[version] 1723 else: 1724 return None 1725 1726 def record_reference_types(self, location, class_types, instance_types, 1727 module_types, constrained, constrained_specific=False, invocations=None): 1728 1729 """ 1730 Associate attribute provider types with the given 'location', consisting 1731 of the given 'class_types', 'instance_types' and 'module_types'. 1732 1733 If 'constrained' is indicated, the constrained nature of the accessor is 1734 recorded for the location. 1735 1736 If 'constrained_specific' is indicated using a true value, instance types 1737 will not be added to class types to permit access via instances at the 1738 given location. This is only useful where a specific accessor is known 1739 to be a class. 1740 1741 If 'invocations' is given, the given attribute names indicate those 1742 which are involved in invocations. Such invocations, if involving 1743 functions, will employ those functions as bound methods and will 1744 therefore not support classes as accessors, only instances of such 1745 classes. 1746 1747 Note that the specified types only indicate the provider types for 1748 attributes, whereas the recorded accessor types indicate the possible 1749 types of the actual objects used to access attributes. 1750 """ 1751 1752 # Update the type details for the location. 1753 1754 self.provider_class_types[location].update(class_types) 1755 self.provider_instance_types[location].update(instance_types) 1756 self.provider_module_types[location].update(module_types) 1757 1758 # Class types support classes and instances as accessors. 1759 # Instance-only and module types support only their own kinds as 1760 # accessors. 1761 1762 path, name, version, attrnames = location 1763 1764 if invocations: 1765 class_only_types = self.filter_for_invocations(class_types, invocations) 1766 else: 1767 class_only_types = class_types 1768 1769 # However, the nature of accessors can be further determined. 1770 # Any self variable may only refer to an instance. 1771 1772 if name != "self" or not self.in_method(path): 1773 self.accessor_class_types[location].update(class_only_types) 1774 1775 if not constrained_specific: 1776 self.accessor_instance_types[location].update(class_types) 1777 1778 self.accessor_instance_types[location].update(instance_types) 1779 1780 if name != "self" or not self.in_method(path): 1781 self.accessor_module_types[location].update(module_types) 1782 1783 if constrained: 1784 self.accessor_constrained.add(location) 1785 1786 def filter_for_invocations(self, class_types, attrnames): 1787 1788 """ 1789 From the given 'class_types', identify methods for the given 1790 'attrnames' that are being invoked, returning a filtered collection of 1791 class types. 1792 1793 This method may be used to remove class types from consideration where 1794 their attributes are methods that are directly invoked: method 1795 invocations must involve instance accessors. 1796 """ 1797 1798 to_filter = set() 1799 1800 for class_type in class_types: 1801 for attrname in attrnames: 1802 1803 # Attempt to obtain a class attribute of the given name. This 1804 # may return an attribute provided by an ancestor class. 1805 1806 ref = self.importer.get_class_attribute(class_type, attrname) 1807 parent_class = ref and ref.parent() 1808 1809 # If such an attribute is a method and would be available on 1810 # the given class, record the class for filtering. 1811 1812 if ref and ref.has_kind("<function>") and ( 1813 parent_class == class_type or 1814 class_type in self.descendants[parent_class]): 1815 1816 to_filter.add(class_type) 1817 break 1818 1819 return set(class_types).difference(to_filter) 1820 1821 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained): 1822 1823 """ 1824 Identify reference attributes, associating them with the given 1825 'location', identifying the given 'attrname', employing the given 1826 'class_types', 'instance_types' and 'module_types'. 1827 1828 If 'constrained' is indicated, the constrained nature of the access is 1829 recorded for the location. 1830 """ 1831 1832 # Record the referenced objects. 1833 1834 self.referenced_attrs[location] = \ 1835 self._identify_reference_attribute(location, attrname, class_types, instance_types, module_types) 1836 1837 if constrained: 1838 self.access_constrained.add(location) 1839 1840 def _identify_reference_attribute(self, location, attrname, class_types, instance_types, module_types): 1841 1842 """ 1843 Identify the reference attribute at the given access 'location', using 1844 the given 'attrname', and employing the given 'class_types', 1845 'instance_types' and 'module_types'. 1846 """ 1847 1848 attrs = set() 1849 1850 # The class types expose class attributes either directly or via 1851 # instances. 1852 1853 for object_type in class_types: 1854 ref = self.importer.get_class_attribute(object_type, attrname) 1855 if ref and self.is_compatible_callable(location, object_type, ref): 1856 attrs.add(("<class>", object_type, ref)) 1857 1858 # Add any distinct instance attributes that would be provided 1859 # by instances also providing indirect class attribute access. 1860 1861 for ref in self.importer.get_instance_attributes(object_type, attrname): 1862 if self.is_compatible_callable(location, object_type, ref): 1863 attrs.add(("<instance>", object_type, ref)) 1864 1865 # The instance-only types expose instance attributes, but although 1866 # classes are excluded as potential accessors (since they do not provide 1867 # the instance attributes), the class types may still provide some 1868 # attributes. 1869 1870 for object_type in instance_types: 1871 instance_attrs = self.importer.get_instance_attributes(object_type, attrname) 1872 1873 if instance_attrs: 1874 for ref in instance_attrs: 1875 if self.is_compatible_callable(location, object_type, ref): 1876 attrs.add(("<instance>", object_type, ref)) 1877 else: 1878 ref = self.importer.get_class_attribute(object_type, attrname) 1879 if ref and self.is_compatible_callable(location, object_type, ref): 1880 attrs.add(("<class>", object_type, ref)) 1881 1882 # Module types expose module attributes for module accessors. 1883 1884 for object_type in module_types: 1885 ref = self.importer.get_module_attribute(object_type, attrname) 1886 if ref and self.is_compatible_callable(location, object_type, ref): 1887 attrs.add(("<module>", object_type, ref)) 1888 1889 return attrs 1890 1891 def is_compatible_callable(self, location, object_type, ref): 1892 1893 """ 1894 Return whether any invocation at 'location' involving an attribute of 1895 'object_type' identified by 'ref' is compatible with any arguments used. 1896 """ 1897 1898 invocation = self.reference_invocations.get(location) 1899 if invocation is None: 1900 return True 1901 1902 objpath = ref.get_origin() 1903 if not objpath: 1904 return True 1905 1906 parameters = self.importer.function_parameters.get(objpath) 1907 if not parameters: 1908 return True 1909 1910 defaults = self.importer.function_defaults.get(objpath) 1911 arguments, keywords = invocation 1912 names = set(parameters) 1913 1914 # Determine whether the specified arguments are 1915 # compatible with the callable signature. 1916 1917 if arguments >= len(parameters) - len(defaults) and \ 1918 arguments <= len(parameters) and \ 1919 names.issuperset(keywords): 1920 1921 return True 1922 else: 1923 init_item(self.reference_invocations_unsuitable, location, set) 1924 self.reference_invocations_unsuitable[location].add(ref) 1925 return False 1926 1927 # Attribute access plan formulation. 1928 1929 class_tests = ( 1930 ("guarded", "specific", "type"), 1931 ("guarded", "common", "type"), 1932 ("test", "specific", "type"), 1933 ("test", "common", "type"), 1934 ) 1935 1936 def get_access_plan(self, location): 1937 1938 """ 1939 Return details of the access at the given 'location'. The details are as 1940 follows: 1941 1942 * the initial accessor (from which accesses will be performed if no 1943 computed static accessor is found) 1944 * details of any test required on the initial accessor 1945 * details of any type employed by the test 1946 * any static accessor (from which accesses will be performed in 1947 preference to the initial accessor) 1948 * attributes needing to be traversed from the base that yield 1949 unambiguous objects 1950 * access modes for each of the unambiguously-traversed attributes 1951 * remaining attributes needing to be tested and traversed 1952 * details of the context 1953 * any test to apply to the context 1954 * the method of obtaining the first attribute 1955 * the method of obtaining the final attribute 1956 * any static final attribute 1957 * the kinds of objects providing the final attribute 1958 """ 1959 1960 const_access = self.const_accesses_rev.get(location) 1961 1962 path, name, attrnames, version = location 1963 remaining = attrnames.split(".") 1964 attrname = remaining[0] 1965 1966 # Obtain reference and accessor information, retaining also distinct 1967 # provider kind details. 1968 1969 attrs = [] 1970 objtypes = [] 1971 provider_kinds = set() 1972 1973 for attrtype, objtype, attr in self.referenced_attrs[location]: 1974 attrs.append(attr) 1975 objtypes.append(objtype) 1976 provider_kinds.add(attrtype) 1977 1978 # Obtain accessor type and kind information. 1979 1980 accessor_types = self.reference_all_accessor_types[location] 1981 accessor_general_types = self.reference_all_accessor_general_types[location] 1982 accessor_kinds = get_kinds(accessor_general_types) 1983 1984 # Determine any guard or test requirements. 1985 1986 constrained = location in self.access_constrained 1987 test = self.reference_test_types[location] 1988 test_type = self.reference_test_accessor_type.get(location) 1989 1990 # Determine the accessor and provider properties. 1991 1992 class_accessor = "<class>" in accessor_kinds 1993 module_accessor = "<module>" in accessor_kinds 1994 instance_accessor = "<instance>" in accessor_kinds 1995 provided_by_class = "<class>" in provider_kinds 1996 provided_by_instance = "<instance>" in provider_kinds 1997 1998 # Determine how attributes may be accessed relative to the accessor. 1999 2000 object_relative = class_accessor or module_accessor or provided_by_instance 2001 class_relative = instance_accessor and provided_by_class 2002 2003 # Identify the last static attribute for context acquisition. 2004 2005 base = None 2006 dynamic_base = None 2007 2008 # Constant accesses have static accessors. 2009 2010 if const_access: 2011 base = len(objtypes) == 1 and first(objtypes) 2012 2013 # Name-based accesses. 2014 2015 elif name: 2016 ref = self.importer.identify("%s.%s" % (path, name)) 2017 2018 # Constant accessors are static. 2019 2020 if ref and ref.static(): 2021 base = ref.get_origin() 2022 2023 # Usage of previously-generated guard and test details. 2024 2025 elif test[:2] == ("constrained", "specific"): 2026 ref = first(accessor_types) 2027 2028 elif test[:2] == ("constrained", "common"): 2029 ref = first(accessor_general_types) 2030 2031 elif test[:2] == ("guarded", "specific"): 2032 ref = first(accessor_types) 2033 2034 elif test[:2] == ("guarded", "common"): 2035 ref = first(accessor_general_types) 2036 2037 # For attribute-based tests, tentatively identify a dynamic base. 2038 # Such tests allow single or multiple kinds of a type. 2039 2040 elif test[0] == "test" and test[1] in ("common", "specific"): 2041 dynamic_base = test_type 2042 2043 # Static accessors. 2044 2045 if not base and test in self.class_tests: 2046 base = ref and ref.get_origin() or dynamic_base 2047 2048 # Accessors that are not static but whose nature is determined. 2049 2050 elif not base and ref: 2051 dynamic_base = ref.get_origin() 2052 2053 # Determine initial accessor details. 2054 2055 accessor = base or dynamic_base 2056 accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None 2057 provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None 2058 2059 # Traverse remaining attributes. 2060 2061 traversed = [] 2062 traversal_modes = [] 2063 2064 while len(attrs) == 1 and not first(attrs).has_kind("<var>"): 2065 attr = first(attrs) 2066 2067 traversed.append(attrname) 2068 traversal_modes.append(accessor_kind == provider_kind and "object" or "class") 2069 2070 # Consume attribute names providing unambiguous attributes. 2071 2072 del remaining[0] 2073 2074 if not remaining: 2075 break 2076 2077 # Update the last static attribute. 2078 2079 if attr.static(): 2080 base = attr.get_origin() 2081 traversed = [] 2082 traversal_modes = [] 2083 2084 # Get the access details. 2085 2086 attrname = remaining[0] 2087 accessor = attr.get_origin() 2088 accessor_kind = attr.get_kind() 2089 provider_kind = self.importer.get_attribute_provider(attr, attrname) 2090 accessor_kinds = [accessor_kind] 2091 provider_kinds = [provider_kind] 2092 2093 # Get the next attribute. 2094 2095 attrs = self.importer.get_attributes(attr, attrname) 2096 2097 # Where many attributes are suggested, no single attribute identity can 2098 # be loaded. 2099 2100 else: 2101 attr = None 2102 2103 # Determine the method of access. 2104 2105 is_assignment = location in self.reference_assignments or const_access in self.reference_assignments 2106 is_invocation = location in self.reference_invocations or const_access in self.reference_invocations 2107 2108 # Identified attribute that must be accessed via its parent. 2109 2110 if attr and attr.get_name() and is_assignment: 2111 final_method = "static-assign"; origin = attr.get_name() 2112 2113 # Static, identified attribute. 2114 2115 elif attr and attr.static(): 2116 final_method = is_assignment and "static-assign" or \ 2117 is_invocation and "static-invoke" or \ 2118 "static" 2119 origin = attr.final() 2120 2121 # All other methods of access involve traversal. 2122 2123 else: 2124 final_method = is_assignment and "assign" or \ 2125 is_invocation and "access-invoke" or \ 2126 "access" 2127 origin = None 2128 2129 # First attribute accessed at a known position via the accessor. 2130 2131 # Static bases support object-relative accesses only. 2132 2133 if base: 2134 first_method = "relative-object" 2135 2136 # Dynamic bases support either object- or class-relative accesses. 2137 2138 elif dynamic_base: 2139 first_method = "relative" + (object_relative and "-object" or "") + \ 2140 (class_relative and "-class" or "") 2141 2142 # The fallback case is always run-time testing and access. 2143 2144 else: 2145 first_method = "check" + (object_relative and "-object" or "") + \ 2146 (class_relative and "-class" or "") 2147 2148 # Determine whether an unbound method is being accessed via an instance, 2149 # requiring a context test. 2150 2151 context_test = "ignore" 2152 2153 # Assignments do not employ the context. 2154 2155 if is_assignment: 2156 pass 2157 2158 # Obtain a selection of possible attributes if no unambiguous attribute 2159 # was identified. 2160 2161 elif not attr: 2162 2163 # Use previously-deduced attributes for a simple ambiguous access. 2164 # Otherwise, use the final attribute name to obtain possible 2165 # attributes. 2166 2167 if len(remaining) > 1: 2168 attrname = remaining[-1] 2169 2170 (class_types, 2171 only_instance_types, 2172 module_types) = self.get_types_for_attribute(attrname) 2173 2174 accessor_kinds = set() 2175 provider_kinds = set() 2176 2177 if class_types: 2178 accessor_kinds.add("<class>") 2179 accessor_kinds.add("<instance>") 2180 provider_kinds.add("<class>") 2181 if only_instance_types: 2182 accessor_kinds.add("<instance>") 2183 provider_kinds.add("<instance>") 2184 if module_types: 2185 accessor_kinds.add("<module>") 2186 provider_kinds.add("<module>") 2187 2188 attrs = set() 2189 for type in combine_types(class_types, only_instance_types, module_types): 2190 attrs.update(self.importer.get_attributes(type, attrname)) 2191 2192 always_unbound = True 2193 have_function = False 2194 have_var = False 2195 2196 # Determine whether all attributes are unbound methods and whether 2197 # functions or unidentified attributes occur. 2198 2199 for attr in attrs: 2200 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent() 2201 have_function = have_function or attr.has_kind("<function>") 2202 have_var = have_var or attr.has_kind("<var>") 2203 2204 # Test for class-via-instance accesses. 2205 2206 if accessor_kind == "<instance>" and \ 2207 provider_kind == "<class>": 2208 2209 if always_unbound: 2210 context_test = "replace" 2211 else: 2212 context_test = "test" 2213 2214 # Test for the presence of class-via-instance accesses. 2215 2216 elif "<instance>" in accessor_kinds and \ 2217 "<class>" in provider_kinds and \ 2218 (have_function or have_var): 2219 2220 context_test = "test" 2221 2222 # With an unambiguous attribute, determine whether a test is needed. 2223 2224 elif accessor_kind == "<instance>" and \ 2225 provider_kind == "<class>" and \ 2226 (attr.has_kind("<var>") or 2227 attr.has_kind("<function>") and 2228 attr.name_parent() == attr.parent()): 2229 2230 if attr.has_kind("<var>"): 2231 context_test = "test" 2232 else: 2233 context_test = "replace" 2234 2235 # With an unambiguous attribute with ambiguity in the access method, 2236 # generate a test. 2237 2238 elif "<instance>" in accessor_kinds and \ 2239 "<class>" in provider_kinds and \ 2240 (attr.has_kind("<var>") or 2241 attr.has_kind("<function>") and 2242 attr.name_parent() == attr.parent()): 2243 2244 context_test = "test" 2245 2246 # Determine the nature of the context. 2247 2248 context = context_test == "ignore" and "unset" or \ 2249 len(traversed + remaining) == 1 and \ 2250 (base and "base" or "original-accessor") or \ 2251 "final-accessor" 2252 2253 return name, test, test_type, base, \ 2254 traversed, traversal_modes, remaining, \ 2255 context, context_test, \ 2256 first_method, final_method, \ 2257 origin, accessor_kinds 2258 2259 def initialise_access_instructions(self): 2260 2261 "Expand access plans into instruction sequences." 2262 2263 for access_location, access_plan in self.access_plans.items(): 2264 2265 # Obtain the access details. 2266 2267 name, test, test_type, base, \ 2268 traversed, traversal_modes, attrnames, \ 2269 context, context_test, \ 2270 first_method, final_method, \ 2271 origin, accessor_kinds = access_plan 2272 2273 # Emit instructions by appending them to a list. 2274 2275 instructions = [] 2276 emit = instructions.append 2277 2278 # Identify any static original accessor. 2279 2280 if base: 2281 original_accessor = base 2282 2283 # Employ names as contexts unless the context needs testing and 2284 # potentially updating. In such cases, temporary context storage is 2285 # used instead. 2286 2287 elif name and not (context_test == "test" and 2288 final_method in ("access-invoke", "static-invoke")): 2289 original_accessor = "<name>" # refers to the name 2290 2291 # Use a generic placeholder representing the access expression in 2292 # the general case. 2293 2294 else: 2295 original_accessor = "<expr>" 2296 2297 # Prepare for any first attribute access. 2298 2299 if traversed: 2300 attrname = traversed[0] 2301 del traversed[0] 2302 elif attrnames: 2303 attrname = attrnames[0] 2304 del attrnames[0] 2305 2306 # Perform the first access explicitly if at least one operation 2307 # requires it. 2308 2309 access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames 2310 2311 # Determine whether the first access involves assignment. 2312 2313 assigning = not traversed and not attrnames and final_method == "assign" 2314 set_accessor = assigning and "<set_target_accessor>" or "<set_accessor>" 2315 stored_accessor = assigning and "<target_accessor>" or "<accessor>" 2316 2317 # Set the context if already available. 2318 2319 context_var = None 2320 2321 if context == "base": 2322 accessor = context_var = (base,) 2323 elif context == "original-accessor": 2324 2325 # Prevent re-evaluation of any dynamic expression by storing it. 2326 2327 if original_accessor == "<expr>": 2328 if final_method in ("access-invoke", "static-invoke"): 2329 emit(("<set_context>", original_accessor)) 2330 accessor = context_var = ("<context>",) 2331 else: 2332 emit((set_accessor, original_accessor)) 2333 accessor = context_var = (stored_accessor,) 2334 else: 2335 accessor = context_var = (original_accessor,) 2336 2337 # Assigning does not set the context. 2338 2339 elif context in ("final-accessor", "unset") and access_first_attribute: 2340 2341 # Prevent re-evaluation of any dynamic expression by storing it. 2342 2343 if original_accessor == "<expr>": 2344 emit((set_accessor, original_accessor)) 2345 accessor = (stored_accessor,) 2346 else: 2347 accessor = (original_accessor,) 2348 2349 # Apply any test. 2350 2351 if test[0] == "test": 2352 accessor = ("__%s_%s_%s" % test, accessor, test_type) 2353 2354 # Perform the first or final access. 2355 # The access only needs performing if the resulting accessor is used. 2356 2357 remaining = len(traversed + attrnames) 2358 2359 if access_first_attribute: 2360 2361 if first_method == "relative-class": 2362 if assigning: 2363 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2364 else: 2365 accessor = ("__load_via_class", accessor, attrname) 2366 2367 elif first_method == "relative-object": 2368 if assigning: 2369 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2370 else: 2371 accessor = ("__load_via_object", accessor, attrname) 2372 2373 elif first_method == "relative-object-class": 2374 if assigning: 2375 emit(("__get_class_and_store", accessor, attrname, "<assexpr>")) 2376 else: 2377 accessor = ("__get_class_and_load", accessor, attrname) 2378 2379 elif first_method == "check-class": 2380 if assigning: 2381 emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>")) 2382 else: 2383 accessor = ("__check_and_load_via_class", accessor, attrname) 2384 2385 elif first_method == "check-object": 2386 if assigning: 2387 emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>")) 2388 else: 2389 accessor = ("__check_and_load_via_object", accessor, attrname) 2390 2391 elif first_method == "check-object-class": 2392 if assigning: 2393 emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) 2394 else: 2395 accessor = ("__check_and_load_via_any", accessor, attrname) 2396 2397 # Traverse attributes using the accessor. 2398 2399 if traversed: 2400 for attrname, traversal_mode in zip(traversed, traversal_modes): 2401 assigning = remaining == 1 and final_method == "assign" 2402 2403 # Set the context, if appropriate. 2404 2405 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2406 2407 # Invoked attributes employ a separate context accessed 2408 # during invocation. 2409 2410 if final_method in ("access-invoke", "static-invoke"): 2411 emit(("<set_context>", accessor)) 2412 accessor = context_var = "<context>" 2413 2414 # A private context within the access is otherwise 2415 # retained. 2416 2417 else: 2418 emit(("<set_private_context>", accessor)) 2419 accessor = context_var = "<private_context>" 2420 2421 # Perform the access only if not achieved directly. 2422 2423 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2424 2425 if traversal_mode == "class": 2426 if assigning: 2427 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2428 else: 2429 accessor = ("__load_via_class", accessor, attrname) 2430 else: 2431 if assigning: 2432 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2433 else: 2434 accessor = ("__load_via_object", accessor, attrname) 2435 2436 remaining -= 1 2437 2438 if attrnames: 2439 for attrname in attrnames: 2440 assigning = remaining == 1 and final_method == "assign" 2441 2442 # Set the context, if appropriate. 2443 2444 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2445 2446 # Invoked attributes employ a separate context accessed 2447 # during invocation. 2448 2449 if final_method in ("access-invoke", "static-invoke"): 2450 emit(("<set_context>", accessor)) 2451 accessor = context_var = "<context>" 2452 2453 # A private context within the access is otherwise 2454 # retained. 2455 2456 else: 2457 emit(("<set_private_context>", accessor)) 2458 accessor = context_var = "<private_context>" 2459 2460 # Perform the access only if not achieved directly. 2461 2462 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2463 2464 if assigning: 2465 emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) 2466 else: 2467 accessor = ("__check_and_load_via_any", accessor, attrname) 2468 2469 remaining -= 1 2470 2471 # Define or emit the means of accessing the actual target. 2472 2473 # Assignments to known attributes. 2474 2475 if final_method == "static-assign": 2476 parent, attrname = origin.rsplit(".", 1) 2477 emit(("__store_via_object", parent, attrname, "<assexpr>")) 2478 2479 # Invoked attributes employ a separate context. 2480 2481 elif final_method in ("static", "static-invoke"): 2482 accessor = ("__load_static_ignore", origin) 2483 2484 # Wrap accesses in context operations. 2485 2486 if context_test == "test": 2487 2488 # Test and combine the context with static attribute details. 2489 2490 if final_method == "static": 2491 emit(("__load_static_test", context_var, origin)) 2492 2493 # Test the context, storing it separately if required for the 2494 # immediately invoked static attribute. 2495 2496 elif final_method == "static-invoke": 2497 emit(("<test_context_static>", context_var, origin)) 2498 2499 # Test the context, storing it separately if required for an 2500 # immediately invoked attribute. 2501 2502 elif final_method == "access-invoke": 2503 emit(("<test_context_revert>", context_var, accessor)) 2504 2505 # Test the context and update the attribute details if 2506 # appropriate. 2507 2508 else: 2509 emit(("__test_context", context_var, accessor)) 2510 2511 elif context_test == "replace": 2512 2513 # Produce an object with updated context. 2514 2515 if final_method == "static": 2516 emit(("__load_static_replace", context_var, origin)) 2517 2518 # Omit the context update operation where the target is static 2519 # and the context is recorded separately. 2520 2521 elif final_method == "static-invoke": 2522 pass 2523 2524 # If a separate context is used for an immediate invocation, 2525 # produce the attribute details unchanged. 2526 2527 elif final_method == "access-invoke": 2528 emit(accessor) 2529 2530 # Update the context in the attribute details. 2531 2532 else: 2533 emit(("__update_context", context_var, accessor)) 2534 2535 # Omit the accessor for assignments and for invocations of static 2536 # targets. 2537 2538 elif final_method not in ("assign", "static-assign", "static-invoke"): 2539 emit(accessor) 2540 2541 # Produce an advisory instruction regarding the context. 2542 2543 if context_var: 2544 emit(("<context_identity>", context_var)) 2545 2546 self.access_instructions[access_location] = instructions 2547 self.accessor_kinds[access_location] = accessor_kinds 2548 2549 # vim: tabstop=4 expandtab shiftwidth=4