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