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 original_location = (location, original_name, original_attrnames, 0) 1335 1336 if original_location != access_location: 1337 self.const_accesses[original_location] = access_location 1338 self.const_accesses_rev[access_location] = original_location 1339 1340 # Aliased name definitions. All aliases with usage will have been 1341 # defined, but they may be refined according to referenced accesses. 1342 1343 for accessor_location in self.alias_index.keys(): 1344 self.record_types_for_alias(accessor_location) 1345 1346 # Update accesses employing aliases. 1347 1348 for access_location in alias_accesses: 1349 self.record_types_for_access(access_location, self.access_index[access_location]) 1350 1351 def constrain_types(self, path, class_types, instance_types, module_types): 1352 1353 """ 1354 Using the given 'path' to an object, constrain the given 'class_types', 1355 'instance_types' and 'module_types'. 1356 1357 Return the class, instance, module types plus whether the types are 1358 constrained to a specific kind of type. 1359 """ 1360 1361 ref = self.importer.identify(path) 1362 if ref: 1363 1364 # Constrain usage suggestions using the identified object. 1365 1366 if ref.has_kind("<class>"): 1367 return ( 1368 set(class_types).intersection([ref.get_origin()]), [], [], True 1369 ) 1370 elif ref.has_kind("<module>"): 1371 return ( 1372 [], [], set(module_types).intersection([ref.get_origin()]), True 1373 ) 1374 1375 return class_types, instance_types, module_types, False 1376 1377 def get_target_types(self, location, usage): 1378 1379 """ 1380 Return the class, instance and module types constrained for the name at 1381 the given 'location' exhibiting the given 'usage'. Whether the types 1382 have been constrained using contextual information is also indicated, 1383 plus whether the types have been constrained to a specific kind of type. 1384 """ 1385 1386 unit_path, name, attrnames, version = location 1387 have_assignments = get_assigned_attributes(usage) 1388 1389 # Detect any initialised name for the location. 1390 1391 if name: 1392 ref = self.get_initialised_name(location) 1393 if ref: 1394 (class_types, only_instance_types, module_types, 1395 _function_types, _var_types) = separate_types([ref]) 1396 return class_types, only_instance_types, module_types, True, have_assignments 1397 1398 # Retrieve the recorded types for the usage. 1399 1400 class_types = self.get_class_types_for_usage(usage) 1401 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1402 module_types = self.get_module_types_for_usage(usage) 1403 1404 # Merge usage deductions with observations to obtain reference types 1405 # for names involved with attribute accesses. 1406 1407 if not name: 1408 return class_types, only_instance_types, module_types, False, have_assignments 1409 1410 # Obtain references to known objects. 1411 1412 path = get_name_path(unit_path, name) 1413 1414 class_types, only_instance_types, module_types, constrained_specific = \ 1415 self.constrain_types(path, class_types, only_instance_types, module_types) 1416 1417 if constrained_specific: 1418 return class_types, only_instance_types, module_types, constrained_specific, \ 1419 constrained_specific or have_assignments 1420 1421 # Constrain "self" references. 1422 1423 if name == "self": 1424 1425 # Test for the class of the method in the deduced types. 1426 1427 class_name = self.in_method(unit_path) 1428 1429 if class_name and class_name not in class_types and class_name not in only_instance_types: 1430 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." % 1431 (unit_path, encode_usage(usage), class_name)) 1432 1433 # Constrain the types to the class's hierarchy. 1434 1435 t = self.constrain_self_reference(unit_path, class_types, only_instance_types) 1436 if t: 1437 class_types, only_instance_types, module_types, constrained = t 1438 return class_types, only_instance_types, module_types, constrained, have_assignments 1439 1440 return class_types, only_instance_types, module_types, False, have_assignments 1441 1442 def constrain_self_reference(self, unit_path, class_types, only_instance_types): 1443 1444 """ 1445 Where the name "self" appears in a method, attempt to constrain the 1446 classes involved. 1447 1448 Return the class, instance, module types plus whether the types are 1449 constrained. 1450 """ 1451 1452 class_name = self.in_method(unit_path) 1453 1454 if not class_name: 1455 return None 1456 1457 classes = set([class_name]) 1458 classes.update(self.get_descendants_for_class(class_name)) 1459 1460 # Note that only instances will be expected for these references but 1461 # either classes or instances may provide the attributes. 1462 1463 return ( 1464 set(class_types).intersection(classes), 1465 set(only_instance_types).intersection(classes), 1466 [], True 1467 ) 1468 1469 def in_method(self, path): 1470 1471 "Return whether 'path' refers to a method." 1472 1473 class_name, method_name = path.rsplit(".", 1) 1474 return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name 1475 1476 def init_reference_details(self, location): 1477 1478 "Initialise reference-related details for 'location'." 1479 1480 self.init_definition_details(location) 1481 self.init_access_details(location) 1482 1483 def init_definition_details(self, location): 1484 1485 "Initialise name definition details for 'location'." 1486 1487 self.accessor_class_types[location] = set() 1488 self.accessor_instance_types[location] = set() 1489 self.accessor_module_types[location] = set() 1490 self.provider_class_types[location] = set() 1491 self.provider_instance_types[location] = set() 1492 self.provider_module_types[location] = set() 1493 1494 def init_access_details(self, location): 1495 1496 "Initialise access details at 'location'." 1497 1498 self.referenced_attrs[location] = {} 1499 1500 def record_types_for_access(self, access_location, accessor_locations, alias_accesses=None): 1501 1502 """ 1503 Define types for the 'access_location' associated with the given 1504 'accessor_locations'. 1505 """ 1506 1507 attrname = get_attrname_from_location(access_location) 1508 if not attrname: 1509 return 1510 1511 # Collect all suggested types for the accessors. Accesses may 1512 # require accessors from of a subset of the complete set of types. 1513 1514 class_types = set() 1515 module_types = set() 1516 instance_types = set() 1517 1518 constrained = True 1519 1520 for location in accessor_locations: 1521 1522 # Remember accesses employing aliases. 1523 1524 if alias_accesses is not None and self.alias_index.has_key(location): 1525 alias_accesses.add(access_location) 1526 1527 # Use the type information deduced for names from above. 1528 1529 if self.accessor_class_types.has_key(location): 1530 class_types.update(self.accessor_class_types[location]) 1531 module_types.update(self.accessor_module_types[location]) 1532 instance_types.update(self.accessor_instance_types[location]) 1533 1534 # Where accesses are associated with assignments but where no 1535 # attribute usage observations have caused such an association, 1536 # the attribute name is considered by itself. 1537 1538 else: 1539 self.init_definition_details(location) 1540 self.record_types_for_usage(location, [(attrname, False, False)]) 1541 1542 constrained = location in self.accessor_constrained and constrained 1543 1544 self.init_access_details(access_location) 1545 self.identify_reference_attributes(access_location, attrname, class_types, instance_types, module_types, constrained) 1546 1547 def record_types_for_usage(self, accessor_location, usage): 1548 1549 """ 1550 Record types for the given 'accessor_location' according to the given 1551 'usage' observations which may be None to indicate an absence of usage. 1552 """ 1553 1554 (class_types, 1555 instance_types, 1556 module_types, 1557 constrained, 1558 constrained_specific) = self.get_target_types(accessor_location, usage) 1559 1560 invocations = get_invoked_attributes(usage) 1561 1562 self.record_reference_types(accessor_location, class_types, instance_types, 1563 module_types, constrained, constrained_specific, invocations) 1564 1565 def record_types_for_attribute(self, access_location, attrname): 1566 1567 """ 1568 Record types for the 'access_location' employing only the given 1569 'attrname' for type deduction. 1570 """ 1571 1572 (class_types, 1573 only_instance_types, 1574 module_types) = self.get_types_for_attribute(attrname) 1575 1576 self.init_reference_details(access_location) 1577 1578 self.identify_reference_attributes(access_location, attrname, class_types, only_instance_types, module_types, False) 1579 self.record_reference_types(access_location, class_types, only_instance_types, module_types, False) 1580 1581 def get_types_for_attribute(self, attrname): 1582 1583 "Return class, instance-only and module types supporting 'attrname'." 1584 1585 usage = ((attrname, False, False),) 1586 1587 class_types = self.get_class_types_for_usage(usage) 1588 only_instance_types = set(self.get_instance_types_for_usage(usage)).difference(class_types) 1589 module_types = self.get_module_types_for_usage(usage) 1590 1591 return class_types, only_instance_types, module_types 1592 1593 def record_types_for_alias(self, accessor_location): 1594 1595 """ 1596 Define types for the 'accessor_location' not having associated usage. 1597 """ 1598 1599 have_access = self.provider_class_types.has_key(accessor_location) 1600 1601 # With an access, attempt to narrow the existing selection of provider 1602 # types. 1603 1604 if have_access: 1605 provider_class_types = self.provider_class_types[accessor_location] 1606 provider_instance_types = self.provider_instance_types[accessor_location] 1607 provider_module_types = self.provider_module_types[accessor_location] 1608 1609 # Find details for any corresponding access. 1610 1611 all_class_types = set() 1612 all_instance_types = set() 1613 all_module_types = set() 1614 1615 for access_location in self.alias_index[accessor_location]: 1616 location, name, attrnames, access_number = access_location 1617 1618 # Alias references an attribute access. 1619 1620 if attrnames: 1621 1622 # Obtain attribute references for the access. 1623 1624 attrs = [] 1625 for _attrtype, object_type, attr in self.referenced_attrs[access_location]: 1626 attrs.append(attr) 1627 1628 # Separate the different attribute types. 1629 1630 (class_types, instance_types, module_types, 1631 function_types, var_types) = separate_types(attrs) 1632 1633 # Where non-accessor types are found, do not attempt to refine 1634 # the defined accessor types. 1635 1636 if function_types or var_types: 1637 return 1638 1639 class_types = set(provider_class_types).intersection(class_types) 1640 instance_types = set(provider_instance_types).intersection(instance_types) 1641 module_types = set(provider_module_types).intersection(module_types) 1642 1643 # Alias references a name, not an access. 1644 1645 else: 1646 # Attempt to refine the types using initialised names. 1647 1648 attr = self.get_initialised_name(access_location) 1649 if attr: 1650 (class_types, instance_types, module_types, 1651 _function_types, _var_types) = separate_types([attr]) 1652 1653 # Where no further information is found, do not attempt to 1654 # refine the defined accessor types. 1655 1656 else: 1657 return 1658 1659 all_class_types.update(class_types) 1660 all_instance_types.update(instance_types) 1661 all_module_types.update(module_types) 1662 1663 # Record refined type details for the alias as an accessor. 1664 1665 self.init_definition_details(accessor_location) 1666 self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False) 1667 1668 # Without an access, attempt to identify references for the alias. 1669 1670 else: 1671 refs = set() 1672 1673 for access_location in self.alias_index[accessor_location]: 1674 1675 # Obtain any redefined constant access location. 1676 1677 if self.const_accesses.has_key(access_location): 1678 access_location = self.const_accesses[access_location] 1679 1680 location, name, attrnames, access_number = access_location 1681 1682 # Alias references an attribute access. 1683 1684 if attrnames: 1685 attrs = [] 1686 for attrtype, object_type, attr in self.referenced_attrs[access_location]: 1687 attrs.append(attr) 1688 refs.update(attrs) 1689 1690 # Alias references a name, not an access. 1691 1692 else: 1693 attr = self.get_initialised_name(access_location) 1694 attrs = attr and [attr] or [] 1695 if not attrs and self.provider_class_types.has_key(access_location): 1696 class_types = self.provider_class_types[access_location] 1697 instance_types = self.provider_instance_types[access_location] 1698 module_types = self.provider_module_types[access_location] 1699 attrs = combine_types(class_types, instance_types, module_types) 1700 if attrs: 1701 refs.update(attrs) 1702 1703 # Record reference details for the alias separately from accessors. 1704 1705 self.referenced_objects[accessor_location] = refs 1706 1707 def get_initialised_name(self, access_location): 1708 1709 """ 1710 Return references for any initialised names at 'access_location', or 1711 None if no such references exist. 1712 """ 1713 1714 location, name, attrnames, version = access_location 1715 path = get_name_path(location, name) 1716 1717 # Use initialiser information, if available. 1718 1719 refs = self.importer.all_initialised_names.get(path) 1720 if refs and refs.has_key(version): 1721 return refs[version] 1722 else: 1723 return None 1724 1725 def record_reference_types(self, location, class_types, instance_types, 1726 module_types, constrained, constrained_specific=False, invocations=None): 1727 1728 """ 1729 Associate attribute provider types with the given 'location', consisting 1730 of the given 'class_types', 'instance_types' and 'module_types'. 1731 1732 If 'constrained' is indicated, the constrained nature of the accessor is 1733 recorded for the location. 1734 1735 If 'constrained_specific' is indicated using a true value, instance types 1736 will not be added to class types to permit access via instances at the 1737 given location. This is only useful where a specific accessor is known 1738 to be a class. 1739 1740 If 'invocations' is given, the given attribute names indicate those 1741 which are involved in invocations. Such invocations, if involving 1742 functions, will employ those functions as bound methods and will 1743 therefore not support classes as accessors, only instances of such 1744 classes. 1745 1746 Note that the specified types only indicate the provider types for 1747 attributes, whereas the recorded accessor types indicate the possible 1748 types of the actual objects used to access attributes. 1749 """ 1750 1751 # Update the type details for the location. 1752 1753 self.provider_class_types[location].update(class_types) 1754 self.provider_instance_types[location].update(instance_types) 1755 self.provider_module_types[location].update(module_types) 1756 1757 # Class types support classes and instances as accessors. 1758 # Instance-only and module types support only their own kinds as 1759 # accessors. 1760 1761 path, name, version, attrnames = location 1762 1763 if invocations: 1764 class_only_types = self.filter_for_invocations(class_types, invocations) 1765 else: 1766 class_only_types = class_types 1767 1768 # However, the nature of accessors can be further determined. 1769 # Any self variable may only refer to an instance. 1770 1771 if name != "self" or not self.in_method(path): 1772 self.accessor_class_types[location].update(class_only_types) 1773 1774 if not constrained_specific: 1775 self.accessor_instance_types[location].update(class_types) 1776 1777 self.accessor_instance_types[location].update(instance_types) 1778 1779 if name != "self" or not self.in_method(path): 1780 self.accessor_module_types[location].update(module_types) 1781 1782 if constrained: 1783 self.accessor_constrained.add(location) 1784 1785 def filter_for_invocations(self, class_types, attrnames): 1786 1787 """ 1788 From the given 'class_types', identify methods for the given 1789 'attrnames' that are being invoked, returning a filtered collection of 1790 class types. 1791 1792 This method may be used to remove class types from consideration where 1793 their attributes are methods that are directly invoked: method 1794 invocations must involve instance accessors. 1795 """ 1796 1797 to_filter = set() 1798 1799 for class_type in class_types: 1800 for attrname in attrnames: 1801 1802 # Attempt to obtain a class attribute of the given name. This 1803 # may return an attribute provided by an ancestor class. 1804 1805 ref = self.importer.get_class_attribute(class_type, attrname) 1806 parent_class = ref and ref.parent() 1807 1808 # If such an attribute is a method and would be available on 1809 # the given class, record the class for filtering. 1810 1811 if ref and ref.has_kind("<function>") and ( 1812 parent_class == class_type or 1813 class_type in self.descendants[parent_class]): 1814 1815 to_filter.add(class_type) 1816 break 1817 1818 return set(class_types).difference(to_filter) 1819 1820 def identify_reference_attributes(self, location, attrname, class_types, instance_types, module_types, constrained): 1821 1822 """ 1823 Identify reference attributes, associating them with the given 1824 'location', identifying the given 'attrname', employing the given 1825 'class_types', 'instance_types' and 'module_types'. 1826 1827 If 'constrained' is indicated, the constrained nature of the access is 1828 recorded for the location. 1829 """ 1830 1831 # Record the referenced objects. 1832 1833 self.referenced_attrs[location] = \ 1834 self._identify_reference_attribute(location, attrname, class_types, instance_types, module_types) 1835 1836 if constrained: 1837 self.access_constrained.add(location) 1838 1839 def _identify_reference_attribute(self, location, attrname, class_types, instance_types, module_types): 1840 1841 """ 1842 Identify the reference attribute at the given access 'location', using 1843 the given 'attrname', and employing the given 'class_types', 1844 'instance_types' and 'module_types'. 1845 """ 1846 1847 attrs = set() 1848 1849 # The class types expose class attributes either directly or via 1850 # instances. 1851 1852 for object_type in class_types: 1853 ref = self.importer.get_class_attribute(object_type, attrname) 1854 if ref and self.is_compatible_callable(location, object_type, ref): 1855 attrs.add(("<class>", object_type, ref)) 1856 1857 # Add any distinct instance attributes that would be provided 1858 # by instances also providing indirect class attribute access. 1859 1860 for ref in self.importer.get_instance_attributes(object_type, attrname): 1861 if self.is_compatible_callable(location, object_type, ref): 1862 attrs.add(("<instance>", object_type, ref)) 1863 1864 # The instance-only types expose instance attributes, but although 1865 # classes are excluded as potential accessors (since they do not provide 1866 # the instance attributes), the class types may still provide some 1867 # attributes. 1868 1869 for object_type in instance_types: 1870 instance_attrs = self.importer.get_instance_attributes(object_type, attrname) 1871 1872 if instance_attrs: 1873 for ref in instance_attrs: 1874 if self.is_compatible_callable(location, object_type, ref): 1875 attrs.add(("<instance>", object_type, ref)) 1876 else: 1877 ref = self.importer.get_class_attribute(object_type, attrname) 1878 if ref and self.is_compatible_callable(location, object_type, ref): 1879 attrs.add(("<class>", object_type, ref)) 1880 1881 # Module types expose module attributes for module accessors. 1882 1883 for object_type in module_types: 1884 ref = self.importer.get_module_attribute(object_type, attrname) 1885 if ref and self.is_compatible_callable(location, object_type, ref): 1886 attrs.add(("<module>", object_type, ref)) 1887 1888 return attrs 1889 1890 def is_compatible_callable(self, location, object_type, ref): 1891 1892 """ 1893 Return whether any invocation at 'location' involving an attribute of 1894 'object_type' identified by 'ref' is compatible with any arguments used. 1895 """ 1896 1897 invocation = self.reference_invocations.get(location) 1898 if invocation is None: 1899 return True 1900 1901 objpath = ref.get_origin() 1902 if not objpath: 1903 return True 1904 1905 parameters = self.importer.function_parameters.get(objpath) 1906 if not parameters: 1907 return True 1908 1909 defaults = self.importer.function_defaults.get(objpath) 1910 arguments, keywords = invocation 1911 names = set(parameters) 1912 1913 # Determine whether the specified arguments are 1914 # compatible with the callable signature. 1915 1916 if arguments >= len(parameters) - len(defaults) and \ 1917 arguments <= len(parameters) and \ 1918 names.issuperset(keywords): 1919 1920 return True 1921 else: 1922 init_item(self.reference_invocations_unsuitable, location, set) 1923 self.reference_invocations_unsuitable[location].add(ref) 1924 return False 1925 1926 # Attribute access plan formulation. 1927 1928 class_tests = ( 1929 ("guarded", "specific", "type"), 1930 ("guarded", "common", "type"), 1931 ("test", "specific", "type"), 1932 ("test", "common", "type"), 1933 ) 1934 1935 def get_access_plan(self, location): 1936 1937 """ 1938 Return details of the access at the given 'location'. The details are as 1939 follows: 1940 1941 * the initial accessor (from which accesses will be performed if no 1942 computed static accessor is found) 1943 * details of any test required on the initial accessor 1944 * details of any type employed by the test 1945 * any static accessor (from which accesses will be performed in 1946 preference to the initial accessor) 1947 * attributes needing to be traversed from the base that yield 1948 unambiguous objects 1949 * access modes for each of the unambiguously-traversed attributes 1950 * remaining attributes needing to be tested and traversed 1951 * details of the context 1952 * any test to apply to the context 1953 * the method of obtaining the first attribute 1954 * the method of obtaining the final attribute 1955 * any static final attribute 1956 * the kinds of objects providing the final attribute 1957 """ 1958 1959 const_access = self.const_accesses_rev.get(location) 1960 1961 path, name, attrnames, version = location 1962 remaining = attrnames.split(".") 1963 attrname = remaining[0] 1964 1965 # Obtain reference and accessor information, retaining also distinct 1966 # provider kind details. 1967 1968 attrs = [] 1969 objtypes = [] 1970 provider_kinds = set() 1971 1972 for attrtype, objtype, attr in self.referenced_attrs[location]: 1973 attrs.append(attr) 1974 objtypes.append(objtype) 1975 provider_kinds.add(attrtype) 1976 1977 # Obtain accessor type and kind information. 1978 1979 accessor_types = self.reference_all_accessor_types[location] 1980 accessor_general_types = self.reference_all_accessor_general_types[location] 1981 accessor_kinds = get_kinds(accessor_general_types) 1982 1983 # Determine any guard or test requirements. 1984 1985 constrained = location in self.access_constrained 1986 test = self.reference_test_types[location] 1987 test_type = self.reference_test_accessor_type.get(location) 1988 1989 # Determine the accessor and provider properties. 1990 1991 class_accessor = "<class>" in accessor_kinds 1992 module_accessor = "<module>" in accessor_kinds 1993 instance_accessor = "<instance>" in accessor_kinds 1994 provided_by_class = "<class>" in provider_kinds 1995 provided_by_instance = "<instance>" in provider_kinds 1996 1997 # Determine how attributes may be accessed relative to the accessor. 1998 1999 object_relative = class_accessor or module_accessor or provided_by_instance 2000 class_relative = instance_accessor and provided_by_class 2001 2002 # Identify the last static attribute for context acquisition. 2003 2004 base = None 2005 dynamic_base = None 2006 2007 # Constant accesses have static accessors. 2008 2009 if const_access: 2010 base = len(objtypes) == 1 and first(objtypes) 2011 2012 # Name-based accesses. 2013 2014 elif name: 2015 ref = self.importer.identify("%s.%s" % (path, name)) 2016 2017 # Constant accessors are static. 2018 2019 if ref and ref.static(): 2020 base = ref.get_origin() 2021 2022 # Usage of previously-generated guard and test details. 2023 2024 elif test[:2] == ("constrained", "specific"): 2025 ref = first(accessor_types) 2026 2027 elif test[:2] == ("constrained", "common"): 2028 ref = first(accessor_general_types) 2029 2030 elif test[:2] == ("guarded", "specific"): 2031 ref = first(accessor_types) 2032 2033 elif test[:2] == ("guarded", "common"): 2034 ref = first(accessor_general_types) 2035 2036 # For attribute-based tests, tentatively identify a dynamic base. 2037 # Such tests allow single or multiple kinds of a type. 2038 2039 elif test[0] == "test" and test[1] in ("common", "specific"): 2040 dynamic_base = test_type 2041 2042 # Static accessors. 2043 2044 if not base and test in self.class_tests: 2045 base = ref and ref.get_origin() or dynamic_base 2046 2047 # Accessors that are not static but whose nature is determined. 2048 2049 elif not base and ref: 2050 dynamic_base = ref.get_origin() 2051 2052 # Determine initial accessor details. 2053 2054 accessor = base or dynamic_base 2055 accessor_kind = len(accessor_kinds) == 1 and first(accessor_kinds) or None 2056 provider_kind = len(provider_kinds) == 1 and first(provider_kinds) or None 2057 2058 # Traverse remaining attributes. 2059 2060 traversed = [] 2061 traversal_modes = [] 2062 2063 while len(attrs) == 1 and not first(attrs).has_kind("<var>"): 2064 attr = first(attrs) 2065 2066 traversed.append(attrname) 2067 traversal_modes.append(accessor_kind == provider_kind and "object" or "class") 2068 2069 # Consume attribute names providing unambiguous attributes. 2070 2071 del remaining[0] 2072 2073 if not remaining: 2074 break 2075 2076 # Update the last static attribute. 2077 2078 if attr.static(): 2079 base = attr.get_origin() 2080 traversed = [] 2081 traversal_modes = [] 2082 2083 # Get the access details. 2084 2085 attrname = remaining[0] 2086 accessor = attr.get_origin() 2087 accessor_kind = attr.get_kind() 2088 provider_kind = self.importer.get_attribute_provider(attr, attrname) 2089 accessor_kinds = [accessor_kind] 2090 provider_kinds = [provider_kind] 2091 2092 # Get the next attribute. 2093 2094 attrs = self.importer.get_attributes(attr, attrname) 2095 2096 # Where many attributes are suggested, no single attribute identity can 2097 # be loaded. 2098 2099 else: 2100 attr = None 2101 2102 # Determine the method of access. 2103 2104 is_assignment = location in self.reference_assignments or const_access in self.reference_assignments 2105 is_invocation = location in self.reference_invocations or const_access in self.reference_invocations 2106 2107 # Identified attribute that must be accessed via its parent. 2108 2109 if attr and attr.get_name() and is_assignment: 2110 final_method = "static-assign"; origin = attr.get_name() 2111 2112 # Static, identified attribute. 2113 2114 elif attr and attr.static(): 2115 final_method = is_assignment and "static-assign" or \ 2116 is_invocation and "static-invoke" or \ 2117 "static" 2118 origin = attr.final() 2119 2120 # All other methods of access involve traversal. 2121 2122 else: 2123 final_method = is_assignment and "assign" or \ 2124 is_invocation and "access-invoke" or \ 2125 "access" 2126 origin = None 2127 2128 # First attribute accessed at a known position via the accessor. 2129 2130 # Static bases support object-relative accesses only. 2131 2132 if base: 2133 first_method = "relative-object" 2134 2135 # Dynamic bases support either object- or class-relative accesses. 2136 2137 elif dynamic_base: 2138 first_method = "relative" + (object_relative and "-object" or "") + \ 2139 (class_relative and "-class" or "") 2140 2141 # The fallback case is always run-time testing and access. 2142 2143 else: 2144 first_method = "check" + (object_relative and "-object" or "") + \ 2145 (class_relative and "-class" or "") 2146 2147 # Determine whether an unbound method is being accessed via an instance, 2148 # requiring a context test. 2149 2150 context_test = "ignore" 2151 2152 # Assignments do not employ the context. 2153 2154 if is_assignment: 2155 pass 2156 2157 # Obtain a selection of possible attributes if no unambiguous attribute 2158 # was identified. 2159 2160 elif not attr: 2161 2162 # Use previously-deduced attributes for a simple ambiguous access. 2163 # Otherwise, use the final attribute name to obtain possible 2164 # attributes. 2165 2166 if len(remaining) > 1: 2167 attrname = remaining[-1] 2168 2169 (class_types, 2170 only_instance_types, 2171 module_types) = self.get_types_for_attribute(attrname) 2172 2173 accessor_kinds = set() 2174 provider_kinds = set() 2175 2176 if class_types: 2177 accessor_kinds.add("<class>") 2178 accessor_kinds.add("<instance>") 2179 provider_kinds.add("<class>") 2180 if only_instance_types: 2181 accessor_kinds.add("<instance>") 2182 provider_kinds.add("<instance>") 2183 if module_types: 2184 accessor_kinds.add("<module>") 2185 provider_kinds.add("<module>") 2186 2187 attrs = set() 2188 for type in combine_types(class_types, only_instance_types, module_types): 2189 attrs.update(self.importer.get_attributes(type, attrname)) 2190 2191 always_unbound = True 2192 have_function = False 2193 have_var = False 2194 2195 # Determine whether all attributes are unbound methods and whether 2196 # functions or unidentified attributes occur. 2197 2198 for attr in attrs: 2199 always_unbound = always_unbound and attr.has_kind("<function>") and attr.name_parent() == attr.parent() 2200 have_function = have_function or attr.has_kind("<function>") 2201 have_var = have_var or attr.has_kind("<var>") 2202 2203 # Test for class-via-instance accesses. 2204 2205 if accessor_kind == "<instance>" and \ 2206 provider_kind == "<class>": 2207 2208 if always_unbound: 2209 context_test = "replace" 2210 else: 2211 context_test = "test" 2212 2213 # Test for the presence of class-via-instance accesses. 2214 2215 elif "<instance>" in accessor_kinds and \ 2216 "<class>" in provider_kinds and \ 2217 (have_function or have_var): 2218 2219 context_test = "test" 2220 2221 # With an unambiguous attribute, determine whether a test is needed. 2222 2223 elif accessor_kind == "<instance>" and \ 2224 provider_kind == "<class>" and \ 2225 (attr.has_kind("<var>") or 2226 attr.has_kind("<function>") and 2227 attr.name_parent() == attr.parent()): 2228 2229 if attr.has_kind("<var>"): 2230 context_test = "test" 2231 else: 2232 context_test = "replace" 2233 2234 # With an unambiguous attribute with ambiguity in the access method, 2235 # generate a test. 2236 2237 elif "<instance>" in accessor_kinds and \ 2238 "<class>" in provider_kinds and \ 2239 (attr.has_kind("<var>") or 2240 attr.has_kind("<function>") and 2241 attr.name_parent() == attr.parent()): 2242 2243 context_test = "test" 2244 2245 # Determine the nature of the context. 2246 2247 context = context_test == "ignore" and "unset" or \ 2248 len(traversed + remaining) == 1 and \ 2249 (base and "base" or "original-accessor") or \ 2250 "final-accessor" 2251 2252 return name, test, test_type, base, \ 2253 traversed, traversal_modes, remaining, \ 2254 context, context_test, \ 2255 first_method, final_method, \ 2256 origin, accessor_kinds 2257 2258 def initialise_access_instructions(self): 2259 2260 "Expand access plans into instruction sequences." 2261 2262 for access_location, access_plan in self.access_plans.items(): 2263 2264 # Obtain the access details. 2265 2266 name, test, test_type, base, \ 2267 traversed, traversal_modes, attrnames, \ 2268 context, context_test, \ 2269 first_method, final_method, \ 2270 origin, accessor_kinds = access_plan 2271 2272 # Emit instructions by appending them to a list. 2273 2274 instructions = [] 2275 emit = instructions.append 2276 2277 # Identify any static original accessor. 2278 2279 if base: 2280 original_accessor = base 2281 2282 # Employ names as contexts unless the context needs testing and 2283 # potentially updating. In such cases, temporary context storage is 2284 # used instead. 2285 2286 elif name and not (context_test == "test" and 2287 final_method in ("access-invoke", "static-invoke")): 2288 original_accessor = "<name>" # refers to the name 2289 2290 # Use a generic placeholder representing the access expression in 2291 # the general case. 2292 2293 else: 2294 original_accessor = "<expr>" 2295 2296 # Prepare for any first attribute access. 2297 2298 if traversed: 2299 attrname = traversed[0] 2300 del traversed[0] 2301 elif attrnames: 2302 attrname = attrnames[0] 2303 del attrnames[0] 2304 2305 # Perform the first access explicitly if at least one operation 2306 # requires it. 2307 2308 access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames 2309 2310 # Determine whether the first access involves assignment. 2311 2312 assigning = not traversed and not attrnames and final_method == "assign" 2313 set_accessor = assigning and "<set_target_accessor>" or "<set_accessor>" 2314 stored_accessor = assigning and "<target_accessor>" or "<accessor>" 2315 2316 # Set the context if already available. 2317 2318 context_var = None 2319 2320 if context == "base": 2321 accessor = context_var = (base,) 2322 elif context == "original-accessor": 2323 2324 # Prevent re-evaluation of any dynamic expression by storing it. 2325 2326 if original_accessor == "<expr>": 2327 if final_method in ("access-invoke", "static-invoke"): 2328 emit(("<set_context>", original_accessor)) 2329 accessor = context_var = ("<context>",) 2330 else: 2331 emit((set_accessor, original_accessor)) 2332 accessor = context_var = (stored_accessor,) 2333 else: 2334 accessor = context_var = (original_accessor,) 2335 2336 # Assigning does not set the context. 2337 2338 elif context in ("final-accessor", "unset") and access_first_attribute: 2339 2340 # Prevent re-evaluation of any dynamic expression by storing it. 2341 2342 if original_accessor == "<expr>": 2343 emit((set_accessor, original_accessor)) 2344 accessor = (stored_accessor,) 2345 else: 2346 accessor = (original_accessor,) 2347 2348 # Apply any test. 2349 2350 if test[0] == "test": 2351 accessor = ("__%s_%s_%s" % test, accessor, test_type) 2352 2353 # Perform the first or final access. 2354 # The access only needs performing if the resulting accessor is used. 2355 2356 remaining = len(traversed + attrnames) 2357 2358 if access_first_attribute: 2359 2360 if first_method == "relative-class": 2361 if assigning: 2362 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2363 else: 2364 accessor = ("__load_via_class", accessor, attrname) 2365 2366 elif first_method == "relative-object": 2367 if assigning: 2368 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2369 else: 2370 accessor = ("__load_via_object", accessor, attrname) 2371 2372 elif first_method == "relative-object-class": 2373 if assigning: 2374 emit(("__get_class_and_store", accessor, attrname, "<assexpr>")) 2375 else: 2376 accessor = ("__get_class_and_load", accessor, attrname) 2377 2378 elif first_method == "check-class": 2379 if assigning: 2380 emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>")) 2381 else: 2382 accessor = ("__check_and_load_via_class", accessor, attrname) 2383 2384 elif first_method == "check-object": 2385 if assigning: 2386 emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>")) 2387 else: 2388 accessor = ("__check_and_load_via_object", accessor, attrname) 2389 2390 elif first_method == "check-object-class": 2391 if assigning: 2392 emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) 2393 else: 2394 accessor = ("__check_and_load_via_any", accessor, attrname) 2395 2396 # Traverse attributes using the accessor. 2397 2398 if traversed: 2399 for attrname, traversal_mode in zip(traversed, traversal_modes): 2400 assigning = remaining == 1 and final_method == "assign" 2401 2402 # Set the context, if appropriate. 2403 2404 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2405 2406 # Invoked attributes employ a separate context accessed 2407 # during invocation. 2408 2409 if final_method in ("access-invoke", "static-invoke"): 2410 emit(("<set_context>", accessor)) 2411 accessor = context_var = "<context>" 2412 2413 # A private context within the access is otherwise 2414 # retained. 2415 2416 else: 2417 emit(("<set_private_context>", accessor)) 2418 accessor = context_var = "<private_context>" 2419 2420 # Perform the access only if not achieved directly. 2421 2422 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2423 2424 if traversal_mode == "class": 2425 if assigning: 2426 emit(("__store_via_class", accessor, attrname, "<assexpr>")) 2427 else: 2428 accessor = ("__load_via_class", accessor, attrname) 2429 else: 2430 if assigning: 2431 emit(("__store_via_object", accessor, attrname, "<assexpr>")) 2432 else: 2433 accessor = ("__load_via_object", accessor, attrname) 2434 2435 remaining -= 1 2436 2437 if attrnames: 2438 for attrname in attrnames: 2439 assigning = remaining == 1 and final_method == "assign" 2440 2441 # Set the context, if appropriate. 2442 2443 if remaining == 1 and final_method != "assign" and context == "final-accessor": 2444 2445 # Invoked attributes employ a separate context accessed 2446 # during invocation. 2447 2448 if final_method in ("access-invoke", "static-invoke"): 2449 emit(("<set_context>", accessor)) 2450 accessor = context_var = "<context>" 2451 2452 # A private context within the access is otherwise 2453 # retained. 2454 2455 else: 2456 emit(("<set_private_context>", accessor)) 2457 accessor = context_var = "<private_context>" 2458 2459 # Perform the access only if not achieved directly. 2460 2461 if remaining > 1 or final_method in ("access", "access-invoke", "assign"): 2462 2463 if assigning: 2464 emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>")) 2465 else: 2466 accessor = ("__check_and_load_via_any", accessor, attrname) 2467 2468 remaining -= 1 2469 2470 # Define or emit the means of accessing the actual target. 2471 2472 # Assignments to known attributes. 2473 2474 if final_method == "static-assign": 2475 parent, attrname = origin.rsplit(".", 1) 2476 emit(("__store_via_object", parent, attrname, "<assexpr>")) 2477 2478 # Invoked attributes employ a separate context. 2479 2480 elif final_method in ("static", "static-invoke"): 2481 accessor = ("__load_static_ignore", origin) 2482 2483 # Wrap accesses in context operations. 2484 2485 if context_test == "test": 2486 2487 # Test and combine the context with static attribute details. 2488 2489 if final_method == "static": 2490 emit(("__load_static_test", context_var, origin)) 2491 2492 # Test the context, storing it separately if required for the 2493 # immediately invoked static attribute. 2494 2495 elif final_method == "static-invoke": 2496 emit(("<test_context_static>", context_var, origin)) 2497 2498 # Test the context, storing it separately if required for an 2499 # immediately invoked attribute. 2500 2501 elif final_method == "access-invoke": 2502 emit(("<test_context_revert>", context_var, accessor)) 2503 2504 # Test the context and update the attribute details if 2505 # appropriate. 2506 2507 else: 2508 emit(("__test_context", context_var, accessor)) 2509 2510 elif context_test == "replace": 2511 2512 # Produce an object with updated context. 2513 2514 if final_method == "static": 2515 emit(("__load_static_replace", context_var, origin)) 2516 2517 # Omit the context update operation where the target is static 2518 # and the context is recorded separately. 2519 2520 elif final_method == "static-invoke": 2521 pass 2522 2523 # If a separate context is used for an immediate invocation, 2524 # produce the attribute details unchanged. 2525 2526 elif final_method == "access-invoke": 2527 emit(accessor) 2528 2529 # Update the context in the attribute details. 2530 2531 else: 2532 emit(("__update_context", context_var, accessor)) 2533 2534 # Omit the accessor for assignments and for invocations of static 2535 # targets. 2536 2537 elif final_method not in ("assign", "static-assign", "static-invoke"): 2538 emit(accessor) 2539 2540 # Produce an advisory instruction regarding the context. 2541 2542 if context_var: 2543 emit(("<context_identity>", context_var)) 2544 2545 self.access_instructions[access_location] = instructions 2546 self.accessor_kinds[access_location] = accessor_kinds 2547 2548 # vim: tabstop=4 expandtab shiftwidth=4