1 #!/usr/bin/env python 2 3 """ 4 Translate the AST of a Python program into a more interpretable representation. 5 6 Copyright (C) 2007, 2008 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 micropython.common import * 23 from micropython.data import * 24 from micropython.rsvp import * 25 import compiler.ast 26 from compiler.visitor import ASTVisitor 27 28 # Program visitors. 29 30 class Translation(ASTVisitor): 31 32 "A translated module." 33 34 supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "constant_test", "stack_access"] 35 36 def __init__(self, module, importer, optimisations=None): 37 38 """ 39 Initialise the translation with an inspected 'module', the 'importer' 40 and optional 'optimisations'. See the 'supported_optimisations' 41 attribute of this class for permitted values. 42 """ 43 44 ASTVisitor.__init__(self) 45 self.visitor = self 46 self.module = module 47 48 # Global program dependencies. 49 50 self.importer = importer 51 self.objtable = self.importer.get_object_table() 52 self.paramtable = self.importer.get_parameter_table() 53 self.builtins = self.importer.modules.get("__builtins__") 54 55 # Desired optimisations. 56 57 self.optimisations = set(optimisations or []) 58 59 # The current unit being translated. 60 61 self.unit = None 62 63 # Wiring within the code. 64 65 self.labels = {} 66 self.label_number = 0 67 self.loop_labels = [] 68 self.exception_labels = [] 69 70 # The code itself. This is limited to the code for a particular block 71 # being processed. Also retained is information about temporary values 72 # and the presumed state of the value stack. 73 74 self.code = None 75 self.temp_position = 0 76 self.stack = [] 77 78 def __repr__(self): 79 return "Translation(%r)" % self.module 80 81 def get_module_code(self): 82 83 "Return the top-level module code." 84 85 self.unit = self.module 86 self.code = [] 87 self.temp_position = self.unit.stack_local_usage 88 self.stack = [] 89 90 if self.module.module is not None: 91 self.dispatch(self.module.module) 92 93 return self.code 94 95 def get_code(self, unit): 96 97 "Return the code for the given 'unit'." 98 99 self.unit = unit 100 self.code = [] 101 self.temp_position = self.unit.stack_local_usage 102 self.stack = [] 103 104 if unit.astnode is not None: 105 self.dispatch(unit.astnode) 106 107 return self.code 108 109 def get_scope(self, name): 110 if self.unit.has_key(name): 111 return "local" 112 elif self.module.has_key(name): 113 return "global" 114 else: 115 return "builtins" 116 117 # Code feature methods. 118 119 def reset_stack(self): 120 121 "Reset the stack for the current unit." 122 123 self.stack = [] 124 125 def adjust_stack(self, op): 126 127 "Adjust the stack according to the effect of 'op'." 128 129 position = len(self.code) 130 stack_level = len(self.stack) 131 132 op.fix_stack(stack_level) 133 effect = op.get_effect() 134 135 if effect == 1: 136 self.stack.append((position, op)) 137 elif effect < 0: 138 while effect != 0: 139 self.stack.pop() 140 effect += 1 141 142 # Update the stack levels. 143 144 self.unit.stack_usage = max(self.unit.stack_usage, stack_level) 145 146 def revert_stack(self, op): 147 148 "Revert the stack affected by 'op'." 149 150 effect = op.get_effect() 151 152 if effect > 0: 153 while effect != 0: 154 self.stack.pop() 155 effect -= 1 156 elif effect < 0: 157 raise ProcessingError, "Cannot remove instructions which reduce the stack." 158 159 def new_label(self): 160 161 "Return a new label object for use with set_label." 162 163 number = self.label_number 164 label = Label(number) 165 self.labels[label] = label 166 self.label_number += 1 167 return label 168 169 def set_label(self, label): 170 171 """ 172 Set the location of 'label' to that within the entire image: the 173 location within the code combined with location of the code unit. 174 """ 175 176 label.location = len(self.code) + self.unit.code_location 177 178 def get_loop_labels(self): 179 return self.loop_labels[-1] 180 181 def add_loop_labels(self, next_label, exit_label): 182 self.loop_labels.append((next_label, exit_label)) 183 184 def drop_loop_labels(self): 185 self.loop_labels.pop() 186 187 def get_exception_labels(self): 188 return self.exception_labels[-1] 189 190 def add_exception_labels(self, handler_label, exit_label): 191 self.exception_labels.append((handler_label, exit_label)) 192 193 def drop_exception_labels(self): 194 self.exception_labels.pop() 195 196 def reserve_temp(self, n): 197 temp_position = self.temp_position 198 self.temp_position += n 199 self.unit.stack_temp_usage = max(self.unit.stack_temp_usage, self.temp_position) 200 return temp_position 201 202 def discard_temp(self, instructions): 203 for temp in instructions: 204 if isinstance(temp, LoadTemp): 205 self.temp_position -= 1 206 207 # Code writing methods. 208 209 def new_op(self, op): 210 211 "Add 'op' to the generated code." 212 213 position = len(self.code) 214 self._optimise_stack_access(op) 215 self.adjust_stack(op) 216 217 self.code.append(op) 218 219 def new_ops(self, ops): 220 221 """ 222 Add copies of 'ops' to the generated code. This is typically used in 223 connection with sequences of temporary storage instructions. 224 """ 225 226 for op in ops: 227 self.new_op(op.copy()) 228 229 def remove_ops(self, n): 230 231 "Remove the last 'n' instructions." 232 233 for i in range(0, n): 234 op = self.code.pop() 235 self.revert_stack(op) 236 237 def replace_op(self, op): 238 239 "Replace the last added instruction with 'op'." 240 241 self.remove_ops(1) 242 self.new_op(op) 243 244 def remove_op_using_stack(self): 245 246 "Remove the instruction which created the top stack position." 247 248 position, op = self.stack.pop() 249 250 # NOTE: For now, just erase the instruction. 251 252 self.code[position] = None 253 254 op = self.code[-1] 255 while op is None: 256 del self.code[-1] 257 if self.code: 258 op = self.code[-1] 259 else: 260 break 261 262 def last_ops(self, n): 263 264 "Return the last 'n' added instructions in reverse chronological order." 265 266 ops = self.code[-n:] 267 ops.reverse() 268 return ops 269 270 def last_op(self): 271 272 "Return the last added instruction." 273 274 try: 275 return self.code[-1] 276 except IndexError: 277 return None 278 279 # Internal helper methods. 280 281 def _visitAttr(self, node, classes): 282 283 """ 284 Visit the attribute-related 'node', generating instructions based on the 285 given 'classes'. 286 """ 287 288 self.dispatch(node.expr) 289 self._generateAttr(node, node.attrname, classes) 290 291 def _generateAttr(self, node, attrname, classes): 292 293 """ 294 Generate code for the access to 'attrname' using the given 'classes'. 295 """ 296 297 AddressInstruction, AttrInstruction, AttrIndexInstruction = classes 298 299 last = self.last_op() 300 301 # Where the last operation (defining the attribute owner) yields a 302 # constant... 303 304 if self._have_constant_input(0): 305 306 # Optimise away the constant storage if appropriate. 307 # The target and value loading operations are removed. 308 309 if self._optimise_constant_storage(AddressInstruction, 1): 310 return 311 312 # Get the details of the access. 313 314 if isinstance(last.attr, Const): 315 target_name = last.attr.value_type_name() 316 else: 317 target = last.attr.value 318 319 if isinstance(target, Const): 320 target_name = target.value_type_name() 321 elif isinstance(target, Instance): 322 target_name = None # skip production of optimised code 323 else: 324 target_name = target.full_name() 325 326 # Only try and discover the position if the target can be resolved. 327 328 if target_name is not None: 329 330 # Access the object table to get the attribute position. 331 332 try: 333 table_entry = self.objtable.table[target_name] 334 except KeyError: 335 raise TranslateError(self.module.full_name(), node, 336 "No object entry exists for target %r." % target_name) 337 338 try: 339 pos = table_entry[attrname] 340 except KeyError: 341 raise TranslateError(self.module.full_name(), node, 342 "No attribute entry exists for name %r in target %r." % (attrname, target_name)) 343 344 # Produce a suitable instruction. 345 346 self.replace_op(AddressInstruction(pos)) 347 return 348 349 # Where the last operation involves the special 'self' name, check to 350 # see if the attribute is acceptably positioned and produce a direct 351 # access to the attribute. 352 353 elif self._optimise_self_access(attrname, (AddressInstruction, AttrInstruction)): 354 return 355 356 # Otherwise, perform a normal operation. 357 358 try: 359 index = self.objtable.get_index(attrname) 360 except self.objtable.TableError: 361 raise TranslateError(self.module.full_name(), node, 362 "No attribute entry exists for name %r." % attrname) 363 364 self.new_op(AttrIndexInstruction(index)) 365 366 def _startCallFunc(self): 367 368 "Record the location of the invocation." 369 370 self.new_op(MakeFrame()) # records the start of the frame 371 372 def _generateCallFunc(self, args, node): 373 374 """ 375 Support a generic function invocation using the given 'args', occurring 376 on the given 'node', where the expression providing the invocation 377 target has just been generated. 378 379 In other situations, the invocation is much simpler and does not need to 380 handle the full flexibility of a typical Python invocation. Internal 381 invocations, such as those employed by operators and certain 382 control-flow mechanisms, use predetermined arguments and arguably do not 383 need to support the same things as the more general invocations. 384 """ 385 386 target, context, temp = self._generateCallFuncContext() 387 self._generateCallFuncArgs(target, context, args, node) 388 return temp 389 390 def _generateCallFuncContext(self): 391 392 """ 393 Produce code which loads and checks the context of the current 394 invocation, the instructions for whose target have already been 395 produced, returning a list of instructions which reference the 396 invocation target. 397 """ 398 399 t = self._optimise_known_target() 400 if t: 401 target, context = t 402 else: 403 target, context = None, None 404 405 # Store the target in temporary storage for subsequent referencing. 406 407 temp = self._optimise_temp_storage() 408 409 # Where a target or context are not known, load the target and context. 410 411 if context is None: 412 self.new_ops(temp) 413 self.new_op(LoadContext()) 414 415 # Check to see if the context is needed for the target. 416 417 self.new_op(CheckContext()) 418 419 # Where an instance is known to be the context, load the context. 420 421 elif isinstance(context, Instance): 422 self.new_ops(temp) 423 self.new_op(LoadContext()) 424 425 # Otherwise omit the context. 426 427 else: 428 pass # NOTE: Class methods should be supported. 429 430 return target, context, temp 431 432 def _generateCallFuncArgs(self, target, context, args, node): 433 434 """ 435 Given invocation 'target' and 'context' information, a list of nodes 436 representing the 'args' (arguments), generate instructions which load 437 the arguments for the invocation defined by the given 'node'. 438 """ 439 440 # Evaluate the arguments. 441 442 employed_positions = set() 443 extra_keywords = [] 444 445 # NOTE: Fix context for self-accessed methods. 446 447 if context is not None and isinstance(context, Instance): 448 ncontext = 1 449 else: 450 ncontext = 0 451 452 first = 1 453 frame_pos = ncontext 454 455 for arg in args: 456 457 # Handle positional and keyword arguments separately. 458 459 if isinstance(arg, compiler.ast.Keyword): 460 461 # Optimise where the target is known now. 462 463 if target is not None: 464 465 # Find the parameter table entry for the target. 466 467 target_name = target.full_name() 468 469 # Look for a callable with the precise target name. 470 471 table_entry = self.paramtable.table[target_name] 472 473 # Look the name up in the parameter table entry. 474 475 try: 476 pos = table_entry[arg.name] 477 478 # Where no position is found, this could be an extra keyword 479 # argument. 480 481 except KeyError: 482 extra_keywords.append(arg) 483 continue 484 485 # Test for illegal conditions. 486 487 if pos in employed_positions: 488 raise TranslateError(self.module.full_name(), node, 489 "Keyword argument %r overwrites parameter %r." % (arg.name, pos)) 490 491 employed_positions.add(pos) 492 493 # Add space for arguments appearing before this one. 494 495 if frame_pos < pos: 496 self.new_op(ReserveFrame(pos - frame_pos)) 497 frame_pos = pos 498 499 # Generate code for the keyword and the positioning 500 # operation. 501 502 self.dispatch(arg.expr) 503 504 # If the position corresponds to the current frame element, 505 # skip generating the store instruction. 506 507 if frame_pos > pos: 508 self.new_op(StoreFrame(pos)) 509 510 # Otherwise, generate the code needed to obtain the details of 511 # the parameter location. 512 513 else: 514 515 # Combine the target details with the name to get the location. 516 # See the access method on the List class. 517 518 try: 519 paramindex = self.paramtable.get_index(arg.name) 520 521 # Where no position is found, this could be an extra keyword 522 # argument. 523 524 except self.paramtable.TableError: 525 extra_keywords.append(arg) 526 continue 527 528 # Generate code for the keyword and the positioning 529 # operation. 530 531 self.dispatch(arg.expr) 532 self.new_op(StoreFrameIndex(paramindex)) 533 534 # use (callable+0)+paramindex+table 535 # checks embedded offset against (callable+0) 536 # moves the top of stack to frame+position 537 538 else: 539 self.dispatch(arg) 540 employed_positions.add(frame_pos + ncontext) 541 542 # Check to see if the first argument is appropriate (compatible with 543 # the # target where methods are being invoked via classes). 544 545 if first and context is None: 546 continue_label = self.new_label() 547 self.new_op(CheckSelf()) 548 self.new_op(JumpIfTrue(continue_label)) 549 550 # Where the context is inappropriate, drop the incomplete frame and 551 # raise an exception. 552 553 self.new_op(DropFrame()) 554 self.dispatch(compiler.ast.Name("TypeError")) 555 self.new_op(RaiseException()) 556 self.set_label(continue_label) 557 558 first = 0 559 frame_pos += 1 560 561 # NOTE: Extra keywords are not supported. 562 # NOTE: Somehow, the above needs to be combined with * arguments. 563 564 # Either test for a complete set of arguments. 565 566 if target is not None: 567 568 # Make sure that enough arguments have been given. 569 570 nargs_max = len(target.positional_names) 571 ndefaults = len(target.defaults) 572 nargs_min = nargs_max - ndefaults 573 574 for i in range(ncontext, nargs_min): 575 if i not in employed_positions: 576 raise TranslateError(self.module.full_name(), node, 577 "Argument %r not supplied for %r: need at least %d arguments." % (i+1, target.name, nargs_min)) 578 579 nargs = len(args) 580 581 if nargs > nargs_max and not target.has_star and not target.has_dstar: 582 raise TranslateError(self.module.full_name(), node, 583 "Too many arguments for %r: need at most %d arguments." % (target.name, nargs_max)) 584 585 # Where defaults are involved, put them into the frame. 586 # Here, we use negative index values to visit the right hand end of 587 # the defaults list. 588 589 for pos in range(nargs_min, nargs_max): 590 if pos not in employed_positions: 591 #self.new_op(LoadConst(target)) 592 #self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) 593 self.new_op(LoadAddress(target.default_attrs[pos - nargs_min])) 594 595 # If the position corresponds to the current frame element, 596 # skip generating the instruction. 597 598 if frame_pos != pos: 599 self.new_op(StoreFrame(pos)) 600 601 frame_pos += 1 602 603 # Or generate instructions to do this at run-time. 604 # NOTE: CheckFrame has to check the number of arguments and to fill in 605 # NOTE: defaults. 606 607 else: 608 self.new_op(CheckFrame()) 609 610 def _endCallFunc(self, temp): 611 612 "Make the invocation and tidy up afterwards." 613 614 self.new_ops(temp) 615 self.new_op(JumpWithFrame()) 616 617 # NOTE: Exception handling required. 618 619 self.new_op(DropFrame()) 620 self.discard_temp(temp) 621 622 def _visitName(self, node, classes): 623 624 """ 625 Visit the name-related 'node', generating instructions based on the 626 given 'classes'. 627 """ 628 629 name = node.name 630 scope = self.get_scope(name) 631 #print self.module.name, node.lineno, name, scope 632 self._generateName(name, scope, classes, node) 633 634 def _generateName(self, name, scope, classes, node): 635 636 """ 637 Generate code for the access to 'name' in 'scope' using the given 638 'classes', and using the given 'node' as the source of the access. 639 """ 640 641 NameInstruction, AddressInstruction = classes 642 643 # Optimise away the constant storage if appropriate. 644 # The target and value loading operations are removed. 645 646 if self._optimise_constant_storage(NameInstruction, 0): 647 return 648 649 if scope == "local": 650 unit = self.unit 651 if isinstance(unit, Function): 652 self.new_op(NameInstruction(unit.all_locals()[name])) 653 elif isinstance(unit, Class): 654 self.new_op(AddressInstruction(unit.all_class_attributes()[name])) 655 elif isinstance(unit, Module): 656 self.new_op(AddressInstruction(unit.module_attributes()[name])) 657 else: 658 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name)) 659 660 elif scope == "global": 661 globals = self.module.module_attributes() 662 if globals.has_key(name): 663 self.new_op(AddressInstruction(globals[name])) 664 else: 665 raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r." % (self.module, name)) 666 667 else: 668 self.new_op(AddressInstruction(self._get_builtin(name, node))) 669 670 def _get_builtin(self, name, node): 671 if self.builtins is not None: 672 try: 673 return self.builtins[name] 674 except KeyError: 675 raise TranslateError(self.module.full_name(), node, "No __builtins__ definition is available for name %r." % name) 676 else: 677 raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name) 678 679 # Optimisation tests. 680 681 def _should_optimise_constant_storage(self): 682 return "constant_storage" in self.optimisations 683 684 def _should_optimise_constant_test(self): 685 return "constant_test" in self.optimisations 686 687 def _should_optimise_known_target(self): 688 return "known_target" in self.optimisations 689 690 def _should_optimise_self_access(self): 691 return "self_access" in self.optimisations 692 693 def _should_optimise_temp_storage(self): 694 return "temp_storage" in self.optimisations 695 696 def _should_optimise_stack_access(self): 697 return "stack_access" in self.optimisations 698 699 def _have_constant_input(self, n): 700 last = self.last_ops(n+1) 701 return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or 702 isinstance(last[n], LoadConst)) 703 704 def _have_known_target(self): 705 return self._have_constant_input(0) 706 707 def _have_self_input(self): 708 last = self.last_op() 709 return isinstance(self.unit, Function) and \ 710 self.unit.is_method() and isinstance(last, LoadName) and \ 711 last.attr.name == "self" 712 713 def _have_temp_compatible_access(self): 714 last = self.last_op() 715 # NOTE: Should expand to cover LoadAttr and LoadAttrIndex, but this 716 # NOTE: would require inspection of the stack operations. 717 return isinstance(last, (LoadName, LoadTemp, LoadAddress, LoadConst)) 718 719 def _have_fixed_sources(self, access): 720 access_ops = [] 721 for i in xrange(0, access): 722 position, op = self.stack[-1 - i] 723 if not isinstance(op, (LoadName, LoadTemp, LoadAddress, LoadConst)): 724 return 0 725 access_ops.append(op) 726 return access_ops 727 728 # Optimisation methods. See the supported_optimisations class attribute. 729 730 def _optimise_temp_storage(self): 731 732 """ 733 Where the next operation would involve storing a value into temporary 734 storage at 'temp_position', record and remove any simple instruction 735 which produced the value to be stored such that instead of subsequently 736 accessing the temporary storage, that instruction is substituted. 737 738 If no optimisation can be achieved, a StoreTemp instruction is produced 739 and the appropriate LoadTemp instruction is returned. 740 741 All returned instructions are provided in a list. 742 """ 743 744 if self._should_optimise_temp_storage() and \ 745 self._have_temp_compatible_access(): 746 747 last = self.last_op() 748 self.remove_ops(1) 749 return [last] 750 else: 751 temp_position = self.reserve_temp(1) 752 self.new_op(StoreTemp(temp_position)) 753 return [LoadTemp(temp_position)] 754 755 def _optimise_constant_storage(self, instruction, n): 756 757 """ 758 Where this operation should store a constant into a target which is 759 also constant, optimise away both operations. 760 """ 761 762 if self._should_optimise_constant_storage() and \ 763 instruction in (StoreAddress, StoreName) and \ 764 self._have_constant_input(n) and \ 765 (n == 0 or self._have_constant_input(n-1)): 766 767 self.remove_ops(n+1) 768 return 1 769 else: 770 return 0 771 772 def _optimise_constant_test(self, instruction): 773 774 """ 775 Where this operation tests the topmost stack value which happens to be 776 a constant against another stack value, merge the last instruction which 777 loaded the constant into the current 'instruction'. 778 """ 779 780 if self._should_optimise_constant_test() and \ 781 instruction is TestIdentity and \ 782 self._have_constant_input(0): 783 784 last = self.last_op() 785 self.replace_op(TestIdentityAddress(last.attr)) 786 return 1 787 else: 788 return 0 789 790 def _optimise_known_target(self): 791 792 """ 793 Where the target of an invocation is known, provide information about it 794 and its context. If a class is being invoked and the conditions are 795 appropriate, get information about the specific initialiser. 796 """ 797 798 if self._should_optimise_known_target() and self._have_known_target(): 799 last = self.last_op() 800 target = last.attr.value 801 context = last.attr.context 802 803 # Handle calls to classes. 804 805 if isinstance(target, Class): 806 target = target.get_instantiator() 807 context = Instance() 808 809 # A special context is chosen to avoid generating unnecessary 810 # context loading and checking instructions. 811 812 return target, context 813 else: 814 return None 815 816 def _optimise_self_access(self, attrname, classes): 817 818 """ 819 Where the provided 'attrname' accesses an attribute which occupies the 820 same position in all possible objects which can be accessed, generate an 821 instruction using one of the given 'classes', accessing the attribute 822 directly. 823 """ 824 825 AddressInstruction, AttrInstruction = classes 826 827 if self._should_optimise_self_access() and self._have_self_input() and \ 828 not self.unit.is_relocated(attrname): 829 830 # Either generate an instruction operating on an instance attribute. 831 832 try: 833 attr = self.unit.parent.instance_attributes()[attrname] 834 self.new_op(AttrInstruction(attr)) 835 836 # Or generate an instruction operating on a class attribute. 837 838 except KeyError: 839 attr = self.unit.parent.all_attributes()[attrname] 840 new_attr = attr.via_instance() 841 self.replace_op(AddressInstruction(new_attr)) 842 843 return 1 844 else: 845 return 0 846 847 def _optimise_stack_access(self, instruction): 848 849 """ 850 Optimise stack access for the given 'instruction', replacing stack 851 operations with instructions directly accessing the required data. 852 """ 853 854 if self._should_optimise_stack_access(): 855 ops = self._have_fixed_sources(instruction.stack_access) 856 if ops: 857 #print "Optimised", instruction.accesses, "->", ops 858 for i in range(0, instruction.stack_access): 859 self.remove_op_using_stack() 860 instruction.accesses = ops 861 862 # Remove the stack side-effects of these accesses. 863 864 for op in ops: 865 op.remove_results() 866 867 # Visitor methods. 868 869 def default(self, node, *args): 870 raise TranslateError(self.module.full_name(), node, "Node class %r is not supported." % node.__class__) 871 872 def dispatch(self, node, *args): 873 return ASTVisitor.dispatch(self, node, *args) 874 875 def _visitUnary(self, node, method): 876 877 """ 878 _t = node.expr 879 try: 880 _result = _t.__pos__() 881 except AttributeError: 882 raise TypeError 883 """ 884 885 end_call_label = self.new_label() 886 end_label = self.new_label() 887 888 # Evaluate and store the operand in temporary storage. 889 890 self.dispatch(node.expr) 891 temp = self._optimise_temp_storage() 892 893 # Produce the invocation. 894 895 self._startCallFunc() 896 self.new_ops(temp) 897 898 # Get the method on temp. 899 900 self._generateAttr(node, method, (LoadAddress, LoadAttr, LoadAttrIndex)) 901 902 # Add exception handling to the method acquisition instructions where 903 # the attribute access cannot be resolved at compile-time. 904 905 if not self._optimise_known_target(): 906 self.dispatch(compiler.ast.Name("AttributeError")) 907 self.new_op(CheckException()) 908 self.new_op(JumpIfTrue(end_call_label)) 909 910 temp_method = self._optimise_temp_storage() 911 912 # Add arguments. 913 # NOTE: No support for defaults. 914 915 self.new_ops(temp) # Explicit context as first argument. 916 self._endCallFunc(temp_method) 917 self.new_op(Jump(end_label)) 918 919 # End method attempt. 920 921 self.set_label(end_call_label) 922 self.new_op(DropFrame()) # From the method call. 923 924 # Raise a TypeError. 925 926 self.dispatch(compiler.ast.Name("TypeError")) 927 self.new_op(RaiseException()) 928 929 self.set_label(end_label) 930 931 # Compilation duties... 932 933 self.discard_temp(temp) 934 935 def _visitBinary(self, node, left_method, right_method): 936 937 """ 938 _t1 = node.left 939 _t2 = node.right 940 try: 941 _result = _t1.__add__(_t2) 942 if _result is NotImplemented: 943 raise AttributeError 944 except AttributeError: 945 try: 946 _result = _t2.__radd__(_t1) 947 if _result is NotImplemented: 948 raise AttributeError 949 except AttributeError: 950 raise TypeError 951 """ 952 953 end_left_label = self.new_label() 954 right_label = self.new_label() 955 end_right_label = self.new_label() 956 type_error_label = self.new_label() 957 end_label = self.new_label() 958 959 # Evaluate and store the left operand in temporary storage. 960 961 self.dispatch(node.left) 962 temp1 = self._optimise_temp_storage() 963 964 # Evaluate and store the right operand in temporary storage. 965 966 self.dispatch(node.right) 967 temp2 = self._optimise_temp_storage() 968 969 # Left method. 970 971 self._startCallFunc() 972 self.new_ops(temp1) 973 974 # Get left method on temp1. 975 976 self._generateAttr(node, left_method, (LoadAddress, LoadAttr, LoadAttrIndex)) 977 978 # Add exception handling to the method acquisition instructions where 979 # the attribute access cannot be resolved at compile-time. 980 981 if not self._optimise_known_target(): 982 self.dispatch(compiler.ast.Name("AttributeError")) 983 self.new_op(CheckException()) 984 self.new_op(JumpIfTrue(end_left_label)) 985 986 temp_method = self._optimise_temp_storage() 987 988 # Add arguments. 989 # NOTE: No support for defaults. 990 991 self.new_ops(temp1) # Explicit context as first argument. 992 self.new_ops(temp2) 993 self._endCallFunc(temp_method) 994 995 # Test for NotImplemented. 996 # Don't actually raise an exception. 997 998 self.dispatch(compiler.ast.Name("NotImplemented")) 999 if not self._optimise_constant_test(TestIdentity): 1000 self.new_op(TestIdentity()) 1001 self.new_op(JumpIfTrue(right_label)) 1002 self.new_op(Jump(end_label)) 1003 1004 # End left method attempt. 1005 1006 self.set_label(end_left_label) 1007 self.new_op(DropFrame()) # From the left method call. 1008 1009 # Right method. 1010 1011 self.set_label(right_label) 1012 self._startCallFunc() 1013 self.new_ops(temp2) 1014 1015 # Get right method on temp2. 1016 1017 self._generateAttr(node, right_method, (LoadAddress, LoadAttr, LoadAttrIndex)) 1018 1019 # Add exception handling to the method acquisition instructions where 1020 # the attribute access cannot be resolved at compile-time. 1021 1022 if not self._optimise_known_target(): 1023 self.dispatch(compiler.ast.Name("AttributeError")) 1024 self.new_op(CheckException()) 1025 self.new_op(JumpIfTrue(end_right_label)) 1026 1027 temp_method = self._optimise_temp_storage() 1028 1029 # Add arguments. 1030 # NOTE: No support for defaults. 1031 1032 self.new_ops(temp2) # Explicit context as first argument. 1033 self.new_ops(temp1) 1034 self._endCallFunc(temp_method) 1035 1036 # Test for NotImplemented. 1037 # Don't actually raise an exception. 1038 1039 self.dispatch(compiler.ast.Name("NotImplemented")) 1040 if not self._optimise_constant_test(TestIdentity): 1041 self.new_op(TestIdentity()) 1042 self.new_op(JumpIfTrue(type_error_label)) 1043 self.new_op(Jump(end_label)) 1044 1045 # End right method attempt. 1046 1047 self.set_label(end_right_label) 1048 self.new_op(DropFrame()) # From the right method call. 1049 1050 # Raise a TypeError. 1051 1052 self.set_label(type_error_label) 1053 self.dispatch(compiler.ast.Name("TypeError")) 1054 self.new_op(RaiseException()) 1055 1056 self.set_label(end_label) 1057 1058 # Compilation duties... 1059 1060 self.discard_temp(temp1) 1061 self.discard_temp(temp2) 1062 1063 def visitAdd(self, node): 1064 self._visitBinary(node, "__add__", "__radd__") 1065 1066 def visitAnd(self, node): pass 1067 1068 def visitAssert(self, node): pass 1069 1070 def visitAssign(self, node): 1071 self.dispatch(node.expr) 1072 for n in node.nodes: 1073 self.dispatch(n) 1074 1075 def visitAssAttr(self, node): 1076 self._visitAttr(node, (StoreAddress, StoreAttr, StoreAttrIndex)) 1077 1078 def visitAssList(self, node): pass 1079 1080 def visitAssName(self, node): 1081 self._visitName(node, (StoreName, StoreAddress)) 1082 1083 visitAssTuple = visitAssList 1084 1085 def visitAugAssign(self, node): pass 1086 1087 def visitBackquote(self, node): pass 1088 1089 def visitBitand(self, node): 1090 self._visitBinary(node, "__and__", "__rand__") 1091 1092 def visitBitor(self, node): 1093 self._visitBinary(node, "__or__", "__ror__") 1094 1095 def visitBitxor(self, node): 1096 self._visitBinary(node, "__xor__", "__rxor__") 1097 1098 def visitBreak(self, node): 1099 next_label, exit_label = self.get_loop_labels() 1100 self.new_op(Jump(exit_label)) 1101 1102 def visitCallFunc(self, node): 1103 1104 """ 1105 Evaluate positional arguments, evaluate and store keyword arguments in 1106 the correct location, then invoke the function. 1107 """ 1108 1109 # Mark the frame, evaluate the target, generate the call. 1110 1111 self._startCallFunc() 1112 self.dispatch(node.node) 1113 temp = self._generateCallFunc(node.args, node) 1114 self._endCallFunc(temp) 1115 1116 def visitClass(self, node): 1117 1118 # Store the name. 1119 1120 self.new_op(LoadConst(node.unit)) 1121 self._visitName(node, (StoreName, StoreAddress)) 1122 1123 # Visit the code. 1124 1125 unit = self.unit 1126 self.unit = node.unit 1127 self.unit.code_location = self.module.code_location # class body code is not independently addressable 1128 self.dispatch(node.code) 1129 self.unit = unit 1130 1131 def visitCompare(self, node): 1132 1133 """ 1134 self.dispatch(node.expr) 1135 for op_name, next_node in compare.ops: 1136 methods = self.comparison_methods[op_name] 1137 if methods is not None: 1138 # Generate method call using evaluated argument and next node. 1139 else: 1140 # Deal with the special operators. 1141 # Provide short-circuiting. 1142 """ 1143 1144 def visitConst(self, node): 1145 const = self.module.constant_values[node.value] 1146 self.new_op(LoadConst(const)) 1147 1148 def visitContinue(self, node): 1149 next_label, exit_label = self.get_loop_labels() 1150 self.new_op(Jump(next_label)) 1151 1152 def visitDecorators(self, node): pass 1153 1154 def visitDict(self, node): pass 1155 1156 def visitDiscard(self, node): 1157 self.dispatch(node.expr) 1158 1159 def visitDiv(self, node): 1160 self._visitBinary(node, "__div__", "__rdiv__") 1161 1162 def visitEllipsis(self, node): pass 1163 1164 def visitExec(self, node): pass 1165 1166 def visitExpression(self, node): pass 1167 1168 def visitFloorDiv(self, node): 1169 self._visitBinary(node, "__floordiv__", "__rfloordiv__") 1170 1171 def visitFor(self, node): 1172 exit_label = self.new_label() 1173 next_label = self.new_label() 1174 else_label = self.new_label() 1175 1176 # Get the "list" to be iterated over, obtain its iterator. 1177 1178 self._startCallFunc() 1179 self.dispatch(node.list) 1180 self._generateAttr(node, "__iter__", (LoadAddress, LoadAttr, LoadAttrIndex)) 1181 temp = self._generateCallFunc([], node) 1182 self._endCallFunc(temp) 1183 1184 # Iterator on stack. 1185 1186 # In the loop... 1187 1188 self.set_label(next_label) 1189 1190 # Use the iterator to get the next value. 1191 1192 self._startCallFunc() 1193 self.new_op(Duplicate()) 1194 self._generateAttr(node, "next", (LoadAddress, LoadAttr, LoadAttrIndex)) 1195 temp = self._generateCallFunc([], node) 1196 self._endCallFunc(temp) 1197 1198 # Test for StopIteration. 1199 1200 self.dispatch(compiler.ast.Name("StopIteration")) 1201 self.new_op(CheckException()) 1202 if node.else_ is not None: 1203 self.new_op(JumpIfTrue(else_label)) 1204 else: 1205 self.new_op(JumpIfTrue(exit_label)) 1206 1207 # Assign to the target. 1208 1209 self.dispatch(node.assign) 1210 1211 # Process the body with the current next and exit points. 1212 1213 self.add_loop_labels(next_label, exit_label) 1214 self.dispatch(node.body) 1215 self.drop_loop_labels() 1216 1217 # Repeat the loop. 1218 1219 self.new_op(Jump(next_label)) 1220 1221 # Produce the "else" section. 1222 1223 if node.else_ is not None: 1224 self.set_label(exit_label) 1225 self.dispatch(node.else_) 1226 1227 # Pop the iterator. 1228 1229 self.set_label(exit_label) 1230 1231 def visitFrom(self, node): pass 1232 1233 def visitFunction(self, node): 1234 1235 # Only store the name when visiting this node from outside. 1236 1237 if self.unit is not node.unit: 1238 self.new_op(LoadConst(node.unit)) 1239 self._visitName(node, (StoreName, StoreAddress)) 1240 1241 # Generate the default initialisation code. 1242 1243 for attr, default in zip(node.unit.default_attrs, node.unit.defaults): 1244 self.dispatch(default) 1245 self.new_op(StoreAddress(attr)) 1246 1247 # Visiting of the code occurs when get_code is invoked on this node. 1248 1249 else: 1250 self.dispatch(node.code) 1251 if not isinstance(self.last_op(), Return): 1252 self.dispatch(compiler.ast.Name("None")) 1253 self.new_op(Return()) 1254 1255 def visitGenExpr(self, node): pass 1256 1257 def visitGenExprFor(self, node): pass 1258 1259 def visitGenExprIf(self, node): pass 1260 1261 def visitGenExprInner(self, node): pass 1262 1263 def visitGetattr(self, node): 1264 self._visitAttr(node, (LoadAddress, LoadAttr, LoadAttrIndex)) 1265 1266 def visitGlobal(self, node): pass 1267 1268 def visitIf(self, node): 1269 first = 1 1270 exit_label = self.new_label() 1271 1272 for test, body in node.tests + [(None, node.else_)]: 1273 if body is None: 1274 break 1275 if not first: 1276 self.set_label(next_label) 1277 if test is not None: 1278 self.dispatch(test) 1279 next_label = self.new_label() 1280 self.new_op(JumpIfFalse(next_label)) 1281 self.dispatch(body) 1282 self.new_op(Jump(exit_label)) 1283 first = 0 1284 1285 self.set_label(exit_label) 1286 1287 def visitImport(self, node): pass 1288 1289 def visitInvert(self, node): 1290 self._visitUnary(node, "__invert__") 1291 1292 def visitKeyword(self, node): pass 1293 1294 def visitLambda(self, node): pass 1295 1296 def visitLeftShift(self, node): 1297 self._visitBinary(node, "__lshift__", "__rlshift__") 1298 1299 def visitList(self, node): pass 1300 1301 def visitListComp(self, node): pass 1302 1303 def visitListCompFor(self, node): pass 1304 1305 def visitListCompIf(self, node): pass 1306 1307 def visitMod(self, node): 1308 self._visitBinary(node, "__mod__", "__rmod__") 1309 1310 def visitModule(self, node): 1311 self.dispatch(node.node) 1312 1313 def visitMul(self, node): 1314 self._visitBinary(node, "__mul__", "__rmul__") 1315 1316 def visitName(self, node): 1317 if node.name == "None": 1318 const = self.module.constant_values[None] 1319 self.new_op(LoadConst(const)) 1320 else: 1321 self._visitName(node, (LoadName, LoadAddress)) 1322 1323 def visitNot(self, node): pass 1324 1325 def visitOr(self, node): pass 1326 1327 def visitPass(self, node): pass 1328 1329 def visitPower(self, node): 1330 self._visitBinary(node, "__pow__", "__rpow__") 1331 1332 def visitPrint(self, node): pass 1333 1334 def visitPrintnl(self, node): pass 1335 1336 def visitRaise(self, node): pass 1337 1338 def visitReturn(self, node): 1339 if node.value is not None: 1340 self.dispatch(node.value) 1341 else: 1342 self.dispatch(compiler.ast.Name("None")) 1343 self.new_op(Return()) 1344 1345 def visitRightShift(self, node): 1346 self._visitBinary(node, "__rshift__", "__rrshift__") 1347 1348 def visitSlice(self, node): pass 1349 1350 def visitStmt(self, node): 1351 for n in node.nodes: 1352 self.dispatch(n) 1353 self.reset_stack() 1354 1355 def visitSub(self, node): 1356 self._visitBinary(node, "__sub__", "__rsub__") 1357 1358 def visitSubscript(self, node): pass 1359 1360 def visitTryExcept(self, node): 1361 exit_label = self.new_label() 1362 handler_label = self.new_label() 1363 1364 self.add_exception_labels(handler_label, exit_label) 1365 1366 # Try... 1367 # Produce the code, then jump to the exit. 1368 1369 self.dispatch(node.body) 1370 self.new_op(Jump(exit_label)) 1371 1372 # Start of handlers. 1373 1374 self.set_label(handler_label) 1375 for name, assignment, handler in node.handlers: 1376 next_label = self.new_label() 1377 1378 # Test the given exception against the current exception. 1379 1380 if name is not None: 1381 self.dispatch(name) 1382 self.new_op(CheckException()) 1383 self.new_op(JumpIfFalse(next_label)) 1384 1385 # Handle assignment to exception variable. 1386 1387 if assignment is not None: 1388 self.dispatch(assignment) 1389 1390 # Produce the handler code, then jump to the exit. 1391 1392 self.dispatch(handler) 1393 self.new_op(Jump(exit_label)) 1394 1395 self.set_label(next_label) 1396 1397 # Unhandled exceptions. 1398 1399 self.new_op(RaiseException()) 1400 1401 # After exception 1402 1403 self.set_label(exit_label) 1404 1405 # Optional else clause. 1406 1407 if node.else_ is not None: 1408 self.dispatch(node.else_) 1409 1410 self.drop_exception_labels() 1411 1412 def visitTryFinally(self, node): pass 1413 1414 def visitTuple(self, node): pass 1415 1416 def visitUnaryAdd(self, node): 1417 self._visitUnary(node, "__pos__") 1418 1419 def visitUnarySub(self, node): 1420 self._visitUnary(node, "__neg__") 1421 1422 def visitWhile(self, node): 1423 exit_label = self.new_label() 1424 next_label = self.new_label() 1425 else_label = self.new_label() 1426 1427 self.set_label(next_label) 1428 self.dispatch(node.test) 1429 if node.else_ is not None: 1430 self.new_op(JumpIfFalse(else_label)) 1431 else: 1432 self.new_op(JumpIfFalse(exit_label)) 1433 1434 self.add_loop_labels(next_label, exit_label) 1435 1436 self.dispatch(node.body) 1437 self.new_op(Jump(next_label)) 1438 1439 if node.else_ is not None: 1440 self.set_label(else_label) 1441 self.dispatch(node.else_) 1442 1443 self.set_label(exit_label) 1444 self.drop_loop_labels() 1445 1446 def visitWith(self, node): pass 1447 1448 def visitYield(self, node): pass 1449 1450 # Useful data. 1451 1452 comparison_methods = { 1453 "==" : ("__eq__", "__ne__"), 1454 "!=" : ("__ne__", "__eq__"), 1455 "<" : ("__lt__", "__gt__"), 1456 "<=" : ("__le__", "__ge__"), 1457 ">=" : ("__ge__", "__le__"), 1458 ">" : ("__gt__", "__lt__"), 1459 "is" : None, 1460 "is not" : None, 1461 "in" : None, 1462 "not in" : None 1463 } 1464 1465 # vim: tabstop=4 expandtab shiftwidth=4