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