pycodegen.py revision 2e4cc7e0d84747826d3d2546d1bccd9c40a455c2
1import imp 2import os 3import marshal 4import stat 5import string 6import struct 7import sys 8import types 9from cStringIO import StringIO 10 11from compiler import ast, parse, walk, syntax 12from compiler import pyassem, misc, future, symbols 13from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL 14from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\ 15 CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION 16from compiler.pyassem import TupleArg 17 18# Do we have Python 1.x or Python 2.x? 19try: 20 VERSION = sys.version_info[0] 21except AttributeError: 22 VERSION = 1 23 24callfunc_opcode_info = { 25 # (Have *args, Have **args) : opcode 26 (0,0) : "CALL_FUNCTION", 27 (1,0) : "CALL_FUNCTION_VAR", 28 (0,1) : "CALL_FUNCTION_KW", 29 (1,1) : "CALL_FUNCTION_VAR_KW", 30} 31 32LOOP = 1 33EXCEPT = 2 34TRY_FINALLY = 3 35END_FINALLY = 4 36 37class BlockStack(misc.Stack): 38 __super_init = misc.Stack.__init__ 39 40 def __init__(self): 41 self.__super_init(self) 42 self.loop = None 43 44def compile(filename, display=0): 45 f = open(filename) 46 buf = f.read() 47 f.close() 48 mod = Module(buf, filename) 49 try: 50 mod.compile(display) 51 except SyntaxError, err: 52 raise 53 else: 54 f = open(filename + "c", "wb") 55 mod.dump(f) 56 f.close() 57 58class Module: 59 def __init__(self, source, filename): 60 self.filename = os.path.abspath(filename) 61 self.source = source 62 self.code = None 63 64 def compile(self, display=0): 65 tree = parse(self.source) 66 misc.set_filename(self.filename, tree) 67 syntax.check(tree) 68 gen = ModuleCodeGenerator(tree) 69 if display: 70 import pprint 71 print pprint.pprint(tree) 72 self.code = gen.getCode() 73 74 def dump(self, f): 75 f.write(self.getPycHeader()) 76 marshal.dump(self.code, f) 77 78 MAGIC = imp.get_magic() 79 80 def getPycHeader(self): 81 # compile.c uses marshal to write a long directly, with 82 # calling the interface that would also generate a 1-byte code 83 # to indicate the type of the value. simplest way to get the 84 # same effect is to call marshal and then skip the code. 85 mtime = os.stat(self.filename)[stat.ST_MTIME] 86 mtime = struct.pack('i', mtime) 87 return self.MAGIC + mtime 88 89class LocalNameFinder: 90 """Find local names in scope""" 91 def __init__(self, names=()): 92 self.names = misc.Set() 93 self.globals = misc.Set() 94 for name in names: 95 self.names.add(name) 96 97 # XXX list comprehensions and for loops 98 99 def getLocals(self): 100 for elt in self.globals.elements(): 101 if self.names.has_elt(elt): 102 self.names.remove(elt) 103 return self.names 104 105 def visitDict(self, node): 106 pass 107 108 def visitGlobal(self, node): 109 for name in node.names: 110 self.globals.add(name) 111 112 def visitFunction(self, node): 113 self.names.add(node.name) 114 115 def visitLambda(self, node): 116 pass 117 118 def visitImport(self, node): 119 for name, alias in node.names: 120 self.names.add(alias or name) 121 122 def visitFrom(self, node): 123 for name, alias in node.names: 124 self.names.add(alias or name) 125 126 def visitClass(self, node): 127 self.names.add(node.name) 128 129 def visitAssName(self, node): 130 self.names.add(node.name) 131 132def is_constant_false(node): 133 if isinstance(node, ast.Const): 134 if not node.value: 135 return 1 136 return 0 137 138class CodeGenerator: 139 """Defines basic code generator for Python bytecode 140 141 This class is an abstract base class. Concrete subclasses must 142 define an __init__() that defines self.graph and then calls the 143 __init__() defined in this class. 144 145 The concrete class must also define the class attributes 146 NameFinder, FunctionGen, and ClassGen. These attributes can be 147 defined in the initClass() method, which is a hook for 148 initializing these methods after all the classes have been 149 defined. 150 """ 151 152 optimized = 0 # is namespace access optimized? 153 __initialized = None 154 class_name = None # provide default for instance variable 155 156 def __init__(self): 157 if self.__initialized is None: 158 self.initClass() 159 self.__class__.__initialized = 1 160 self.checkClass() 161 self.locals = misc.Stack() 162 self.setups = misc.Stack() 163 self.curStack = 0 164 self.maxStack = 0 165 self.last_lineno = None 166 self._setupGraphDelegation() 167 self._div_op = "BINARY_DIVIDE" 168 169 # XXX set flags based on future features 170 futures = self.get_module().futures 171 for feature in futures: 172 if feature == "division": 173 self.graph.setFlag(CO_FUTURE_DIVISION) 174 self._div_op = "BINARY_TRUE_DIVIDE" 175 elif feature == "generators": 176 self.graph.setFlag(CO_GENERATOR_ALLOWED) 177 178 def initClass(self): 179 """This method is called once for each class""" 180 181 def checkClass(self): 182 """Verify that class is constructed correctly""" 183 try: 184 assert hasattr(self, 'graph') 185 assert getattr(self, 'NameFinder') 186 assert getattr(self, 'FunctionGen') 187 assert getattr(self, 'ClassGen') 188 except AssertionError, msg: 189 intro = "Bad class construction for %s" % self.__class__.__name__ 190 raise AssertionError, intro 191 192 def _setupGraphDelegation(self): 193 self.emit = self.graph.emit 194 self.newBlock = self.graph.newBlock 195 self.startBlock = self.graph.startBlock 196 self.nextBlock = self.graph.nextBlock 197 self.setDocstring = self.graph.setDocstring 198 199 def getCode(self): 200 """Return a code object""" 201 return self.graph.getCode() 202 203 def mangle(self, name): 204 if self.class_name is not None: 205 return misc.mangle(name, self.class_name) 206 else: 207 return name 208 209 def parseSymbols(self, tree): 210 s = symbols.SymbolVisitor() 211 walk(tree, s) 212 return s.scopes 213 214 def get_module(self): 215 raise RuntimeError, "should be implemented by subclasses" 216 217 # Next five methods handle name access 218 219 def isLocalName(self, name): 220 return self.locals.top().has_elt(name) 221 222 def storeName(self, name): 223 self._nameOp('STORE', name) 224 225 def loadName(self, name): 226 self._nameOp('LOAD', name) 227 228 def delName(self, name): 229 self._nameOp('DELETE', name) 230 231 def _nameOp(self, prefix, name): 232 name = self.mangle(name) 233 scope = self.scope.check_name(name) 234 if scope == SC_LOCAL: 235 if not self.optimized: 236 self.emit(prefix + '_NAME', name) 237 else: 238 self.emit(prefix + '_FAST', name) 239 elif scope == SC_GLOBAL: 240 if not self.optimized: 241 self.emit(prefix + '_NAME', name) 242 else: 243 self.emit(prefix + '_GLOBAL', name) 244 elif scope == SC_FREE or scope == SC_CELL: 245 self.emit(prefix + '_DEREF', name) 246 else: 247 raise RuntimeError, "unsupported scope for var %s: %d" % \ 248 (name, scope) 249 250 def _implicitNameOp(self, prefix, name): 251 """Emit name ops for names generated implicitly by for loops 252 253 The interpreter generates names that start with a period or 254 dollar sign. The symbol table ignores these names because 255 they aren't present in the program text. 256 """ 257 if self.optimized: 258 self.emit(prefix + '_FAST', name) 259 else: 260 self.emit(prefix + '_NAME', name) 261 262 def set_lineno(self, node, force=0): 263 """Emit SET_LINENO if node has lineno attribute and it is 264 different than the last lineno emitted. 265 266 Returns true if SET_LINENO was emitted. 267 268 There are no rules for when an AST node should have a lineno 269 attribute. The transformer and AST code need to be reviewed 270 and a consistent policy implemented and documented. Until 271 then, this method works around missing line numbers. 272 """ 273 lineno = getattr(node, 'lineno', None) 274 if lineno is not None and (lineno != self.last_lineno 275 or force): 276 self.emit('SET_LINENO', lineno) 277 self.last_lineno = lineno 278 return 1 279 return 0 280 281 # The first few visitor methods handle nodes that generator new 282 # code objects. They use class attributes to determine what 283 # specialized code generators to use. 284 285 NameFinder = LocalNameFinder 286 FunctionGen = None 287 ClassGen = None 288 289 def visitModule(self, node): 290 self.scopes = self.parseSymbols(node) 291 self.scope = self.scopes[node] 292 self.emit('SET_LINENO', 0) 293 if node.doc: 294 self.emit('LOAD_CONST', node.doc) 295 self.storeName('__doc__') 296 lnf = walk(node.node, self.NameFinder(), verbose=0) 297 self.locals.push(lnf.getLocals()) 298 self.visit(node.node) 299 self.emit('LOAD_CONST', None) 300 self.emit('RETURN_VALUE') 301 302 def visitFunction(self, node): 303 self._visitFuncOrLambda(node, isLambda=0) 304 if node.doc: 305 self.setDocstring(node.doc) 306 self.storeName(node.name) 307 308 def visitLambda(self, node): 309 self._visitFuncOrLambda(node, isLambda=1) 310 311 def _visitFuncOrLambda(self, node, isLambda=0): 312 gen = self.FunctionGen(node, self.scopes, isLambda, 313 self.class_name, self.get_module()) 314 walk(node.code, gen) 315 gen.finish() 316 self.set_lineno(node) 317 for default in node.defaults: 318 self.visit(default) 319 frees = gen.scope.get_free_vars() 320 if frees: 321 for name in frees: 322 self.emit('LOAD_CLOSURE', name) 323 self.emit('LOAD_CONST', gen) 324 self.emit('MAKE_CLOSURE', len(node.defaults)) 325 else: 326 self.emit('LOAD_CONST', gen) 327 self.emit('MAKE_FUNCTION', len(node.defaults)) 328 329 def visitClass(self, node): 330 gen = self.ClassGen(node, self.scopes, 331 self.get_module()) 332 if node.doc: 333 self.emit('LOAD_CONST', node.doc) 334 self.storeName('__doc__') 335 walk(node.code, gen) 336 gen.finish() 337 self.set_lineno(node) 338 self.emit('LOAD_CONST', node.name) 339 for base in node.bases: 340 self.visit(base) 341 self.emit('BUILD_TUPLE', len(node.bases)) 342 frees = gen.scope.get_free_vars() 343 for name in frees: 344 self.emit('LOAD_CLOSURE', name) 345 self.emit('LOAD_CONST', gen) 346 if frees: 347 self.emit('MAKE_CLOSURE', 0) 348 else: 349 self.emit('MAKE_FUNCTION', 0) 350 self.emit('CALL_FUNCTION', 0) 351 self.emit('BUILD_CLASS') 352 self.storeName(node.name) 353 354 # The rest are standard visitor methods 355 356 # The next few implement control-flow statements 357 358 def visitIf(self, node): 359 end = self.newBlock() 360 numtests = len(node.tests) 361 for i in range(numtests): 362 test, suite = node.tests[i] 363 if is_constant_false(test): 364 # XXX will need to check generator stuff here 365 continue 366 self.set_lineno(test) 367 self.visit(test) 368 nextTest = self.newBlock() 369 self.emit('JUMP_IF_FALSE', nextTest) 370 self.nextBlock() 371 self.emit('POP_TOP') 372 self.visit(suite) 373 self.emit('JUMP_FORWARD', end) 374 self.startBlock(nextTest) 375 self.emit('POP_TOP') 376 if node.else_: 377 self.visit(node.else_) 378 self.nextBlock(end) 379 380 def visitWhile(self, node): 381 self.set_lineno(node) 382 383 loop = self.newBlock() 384 else_ = self.newBlock() 385 386 after = self.newBlock() 387 self.emit('SETUP_LOOP', after) 388 389 self.nextBlock(loop) 390 self.setups.push((LOOP, loop)) 391 392 self.set_lineno(node, force=1) 393 self.visit(node.test) 394 self.emit('JUMP_IF_FALSE', else_ or after) 395 396 self.nextBlock() 397 self.emit('POP_TOP') 398 self.visit(node.body) 399 self.emit('JUMP_ABSOLUTE', loop) 400 401 self.startBlock(else_) # or just the POPs if not else clause 402 self.emit('POP_TOP') 403 self.emit('POP_BLOCK') 404 self.setups.pop() 405 if node.else_: 406 self.visit(node.else_) 407 self.nextBlock(after) 408 409 def visitFor(self, node): 410 start = self.newBlock() 411 anchor = self.newBlock() 412 after = self.newBlock() 413 self.setups.push((LOOP, start)) 414 415 self.set_lineno(node) 416 self.emit('SETUP_LOOP', after) 417 self.visit(node.list) 418 self.emit('GET_ITER') 419 420 self.nextBlock(start) 421 self.set_lineno(node, force=1) 422 self.emit('FOR_ITER', anchor) 423 self.visit(node.assign) 424 self.visit(node.body) 425 self.emit('JUMP_ABSOLUTE', start) 426 self.nextBlock(anchor) 427 self.emit('POP_BLOCK') 428 self.setups.pop() 429 if node.else_: 430 self.visit(node.else_) 431 self.nextBlock(after) 432 433 def visitBreak(self, node): 434 if not self.setups: 435 raise SyntaxError, "'break' outside loop (%s, %d)" % \ 436 (node.filename, node.lineno) 437 self.set_lineno(node) 438 self.emit('BREAK_LOOP') 439 440 def visitContinue(self, node): 441 if not self.setups: 442 raise SyntaxError, "'continue' outside loop (%s, %d)" % \ 443 (node.filename, node.lineno) 444 kind, block = self.setups.top() 445 if kind == LOOP: 446 self.set_lineno(node) 447 self.emit('JUMP_ABSOLUTE', block) 448 self.nextBlock() 449 elif kind == EXCEPT or kind == TRY_FINALLY: 450 self.set_lineno(node) 451 # find the block that starts the loop 452 top = len(self.setups) 453 while top > 0: 454 top = top - 1 455 kind, loop_block = self.setups[top] 456 if kind == LOOP: 457 break 458 if kind != LOOP: 459 raise SyntaxError, "'continue' outside loop (%s, %d)" % \ 460 (node.filename, node.lineno) 461 self.emit('CONTINUE_LOOP', loop_block) 462 self.nextBlock() 463 elif kind == END_FINALLY: 464 msg = "'continue' not allowed inside 'finally' clause (%s, %d)" 465 raise SyntaxError, msg % (node.filename, node.lineno) 466 467 def visitTest(self, node, jump): 468 end = self.newBlock() 469 for child in node.nodes[:-1]: 470 self.visit(child) 471 self.emit(jump, end) 472 self.nextBlock() 473 self.emit('POP_TOP') 474 self.visit(node.nodes[-1]) 475 self.nextBlock(end) 476 477 def visitAnd(self, node): 478 self.visitTest(node, 'JUMP_IF_FALSE') 479 480 def visitOr(self, node): 481 self.visitTest(node, 'JUMP_IF_TRUE') 482 483 def visitCompare(self, node): 484 self.visit(node.expr) 485 cleanup = self.newBlock() 486 for op, code in node.ops[:-1]: 487 self.visit(code) 488 self.emit('DUP_TOP') 489 self.emit('ROT_THREE') 490 self.emit('COMPARE_OP', op) 491 self.emit('JUMP_IF_FALSE', cleanup) 492 self.nextBlock() 493 self.emit('POP_TOP') 494 # now do the last comparison 495 if node.ops: 496 op, code = node.ops[-1] 497 self.visit(code) 498 self.emit('COMPARE_OP', op) 499 if len(node.ops) > 1: 500 end = self.newBlock() 501 self.emit('JUMP_FORWARD', end) 502 self.startBlock(cleanup) 503 self.emit('ROT_TWO') 504 self.emit('POP_TOP') 505 self.nextBlock(end) 506 507 # list comprehensions 508 __list_count = 0 509 510 def visitListComp(self, node): 511 self.set_lineno(node) 512 # setup list 513 append = "$append%d" % self.__list_count 514 self.__list_count = self.__list_count + 1 515 self.emit('BUILD_LIST', 0) 516 self.emit('DUP_TOP') 517 self.emit('LOAD_ATTR', 'append') 518 self._implicitNameOp('STORE', append) 519 520 stack = [] 521 for i, for_ in zip(range(len(node.quals)), node.quals): 522 start, anchor = self.visit(for_) 523 cont = None 524 for if_ in for_.ifs: 525 if cont is None: 526 cont = self.newBlock() 527 self.visit(if_, cont) 528 stack.insert(0, (start, cont, anchor)) 529 530 self._implicitNameOp('LOAD', append) 531 self.visit(node.expr) 532 self.emit('CALL_FUNCTION', 1) 533 self.emit('POP_TOP') 534 535 for start, cont, anchor in stack: 536 if cont: 537 skip_one = self.newBlock() 538 self.emit('JUMP_FORWARD', skip_one) 539 self.startBlock(cont) 540 self.emit('POP_TOP') 541 self.nextBlock(skip_one) 542 self.emit('JUMP_ABSOLUTE', start) 543 self.startBlock(anchor) 544 self._implicitNameOp('DELETE', append) 545 546 self.__list_count = self.__list_count - 1 547 548 def visitListCompFor(self, node): 549 start = self.newBlock() 550 anchor = self.newBlock() 551 552 self.visit(node.list) 553 self.emit('GET_ITER') 554 self.nextBlock(start) 555 self.emit('SET_LINENO', node.lineno) 556 self.emit('FOR_ITER', anchor) 557 self.nextBlock() 558 self.visit(node.assign) 559 return start, anchor 560 561 def visitListCompIf(self, node, branch): 562 self.set_lineno(node, force=1) 563 self.visit(node.test) 564 self.emit('JUMP_IF_FALSE', branch) 565 self.newBlock() 566 self.emit('POP_TOP') 567 568 # exception related 569 570 def visitAssert(self, node): 571 # XXX would be interesting to implement this via a 572 # transformation of the AST before this stage 573 end = self.newBlock() 574 self.set_lineno(node) 575 # XXX __debug__ and AssertionError appear to be special cases 576 # -- they are always loaded as globals even if there are local 577 # names. I guess this is a sort of renaming op. 578 self.emit('LOAD_GLOBAL', '__debug__') 579 self.emit('JUMP_IF_FALSE', end) 580 self.nextBlock() 581 self.emit('POP_TOP') 582 self.visit(node.test) 583 self.emit('JUMP_IF_TRUE', end) 584 self.nextBlock() 585 self.emit('POP_TOP') 586 self.emit('LOAD_GLOBAL', 'AssertionError') 587 if node.fail: 588 self.visit(node.fail) 589 self.emit('RAISE_VARARGS', 2) 590 else: 591 self.emit('RAISE_VARARGS', 1) 592 self.nextBlock(end) 593 self.emit('POP_TOP') 594 595 def visitRaise(self, node): 596 self.set_lineno(node) 597 n = 0 598 if node.expr1: 599 self.visit(node.expr1) 600 n = n + 1 601 if node.expr2: 602 self.visit(node.expr2) 603 n = n + 1 604 if node.expr3: 605 self.visit(node.expr3) 606 n = n + 1 607 self.emit('RAISE_VARARGS', n) 608 609 def visitTryExcept(self, node): 610 body = self.newBlock() 611 handlers = self.newBlock() 612 end = self.newBlock() 613 if node.else_: 614 lElse = self.newBlock() 615 else: 616 lElse = end 617 self.set_lineno(node) 618 self.emit('SETUP_EXCEPT', handlers) 619 self.nextBlock(body) 620 self.setups.push((EXCEPT, body)) 621 self.visit(node.body) 622 self.emit('POP_BLOCK') 623 self.setups.pop() 624 self.emit('JUMP_FORWARD', lElse) 625 self.startBlock(handlers) 626 627 last = len(node.handlers) - 1 628 for i in range(len(node.handlers)): 629 expr, target, body = node.handlers[i] 630 self.set_lineno(expr) 631 if expr: 632 self.emit('DUP_TOP') 633 self.visit(expr) 634 self.emit('COMPARE_OP', 'exception match') 635 next = self.newBlock() 636 self.emit('JUMP_IF_FALSE', next) 637 self.nextBlock() 638 self.emit('POP_TOP') 639 self.emit('POP_TOP') 640 if target: 641 self.visit(target) 642 else: 643 self.emit('POP_TOP') 644 self.emit('POP_TOP') 645 self.visit(body) 646 self.emit('JUMP_FORWARD', end) 647 if expr: 648 self.nextBlock(next) 649 else: 650 self.nextBlock() 651 if expr: # XXX 652 self.emit('POP_TOP') 653 self.emit('END_FINALLY') 654 if node.else_: 655 self.nextBlock(lElse) 656 self.visit(node.else_) 657 self.nextBlock(end) 658 659 def visitTryFinally(self, node): 660 body = self.newBlock() 661 final = self.newBlock() 662 self.set_lineno(node) 663 self.emit('SETUP_FINALLY', final) 664 self.nextBlock(body) 665 self.setups.push((TRY_FINALLY, body)) 666 self.visit(node.body) 667 self.emit('POP_BLOCK') 668 self.setups.pop() 669 self.emit('LOAD_CONST', None) 670 self.nextBlock(final) 671 self.setups.push((END_FINALLY, final)) 672 self.visit(node.final) 673 self.emit('END_FINALLY') 674 self.setups.pop() 675 676 # misc 677 678 def visitDiscard(self, node): 679 self.set_lineno(node) 680 self.visit(node.expr) 681 self.emit('POP_TOP') 682 683 def visitConst(self, node): 684 self.emit('LOAD_CONST', node.value) 685 686 def visitKeyword(self, node): 687 self.emit('LOAD_CONST', node.name) 688 self.visit(node.expr) 689 690 def visitGlobal(self, node): 691 # no code to generate 692 pass 693 694 def visitName(self, node): 695 self.set_lineno(node) 696 self.loadName(node.name) 697 698 def visitPass(self, node): 699 self.set_lineno(node) 700 701 def visitImport(self, node): 702 self.set_lineno(node) 703 for name, alias in node.names: 704 if VERSION > 1: 705 self.emit('LOAD_CONST', None) 706 self.emit('IMPORT_NAME', name) 707 mod = string.split(name, ".")[0] 708 self.storeName(alias or mod) 709 710 def visitFrom(self, node): 711 self.set_lineno(node) 712 fromlist = map(lambda (name, alias): name, node.names) 713 if VERSION > 1: 714 self.emit('LOAD_CONST', tuple(fromlist)) 715 self.emit('IMPORT_NAME', node.modname) 716 for name, alias in node.names: 717 if VERSION > 1: 718 if name == '*': 719 self.namespace = 0 720 self.emit('IMPORT_STAR') 721 # There can only be one name w/ from ... import * 722 assert len(node.names) == 1 723 return 724 else: 725 self.emit('IMPORT_FROM', name) 726 self._resolveDots(name) 727 self.storeName(alias or name) 728 else: 729 self.emit('IMPORT_FROM', name) 730 self.emit('POP_TOP') 731 732 def _resolveDots(self, name): 733 elts = string.split(name, ".") 734 if len(elts) == 1: 735 return 736 for elt in elts[1:]: 737 self.emit('LOAD_ATTR', elt) 738 739 def visitGetattr(self, node): 740 self.visit(node.expr) 741 self.emit('LOAD_ATTR', self.mangle(node.attrname)) 742 743 # next five implement assignments 744 745 def visitAssign(self, node): 746 self.set_lineno(node) 747 self.visit(node.expr) 748 dups = len(node.nodes) - 1 749 for i in range(len(node.nodes)): 750 elt = node.nodes[i] 751 if i < dups: 752 self.emit('DUP_TOP') 753 if isinstance(elt, ast.Node): 754 self.visit(elt) 755 756 def visitAssName(self, node): 757 if node.flags == 'OP_ASSIGN': 758 self.storeName(node.name) 759 elif node.flags == 'OP_DELETE': 760 self.set_lineno(node) 761 self.delName(node.name) 762 else: 763 print "oops", node.flags 764 765 def visitAssAttr(self, node): 766 self.visit(node.expr) 767 if node.flags == 'OP_ASSIGN': 768 self.emit('STORE_ATTR', self.mangle(node.attrname)) 769 elif node.flags == 'OP_DELETE': 770 self.emit('DELETE_ATTR', self.mangle(node.attrname)) 771 else: 772 print "warning: unexpected flags:", node.flags 773 print node 774 775 def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'): 776 if findOp(node) != 'OP_DELETE': 777 self.emit(op, len(node.nodes)) 778 for child in node.nodes: 779 self.visit(child) 780 781 if VERSION > 1: 782 visitAssTuple = _visitAssSequence 783 visitAssList = _visitAssSequence 784 else: 785 def visitAssTuple(self, node): 786 self._visitAssSequence(node, 'UNPACK_TUPLE') 787 788 def visitAssList(self, node): 789 self._visitAssSequence(node, 'UNPACK_LIST') 790 791 # augmented assignment 792 793 def visitAugAssign(self, node): 794 self.set_lineno(node) 795 aug_node = wrap_aug(node.node) 796 self.visit(aug_node, "load") 797 self.visit(node.expr) 798 self.emit(self._augmented_opcode[node.op]) 799 self.visit(aug_node, "store") 800 801 _augmented_opcode = { 802 '+=' : 'INPLACE_ADD', 803 '-=' : 'INPLACE_SUBTRACT', 804 '*=' : 'INPLACE_MULTIPLY', 805 '/=' : 'INPLACE_DIVIDE', 806 '//=': 'INPLACE_FLOOR_DIVIDE', 807 '%=' : 'INPLACE_MODULO', 808 '**=': 'INPLACE_POWER', 809 '>>=': 'INPLACE_RSHIFT', 810 '<<=': 'INPLACE_LSHIFT', 811 '&=' : 'INPLACE_AND', 812 '^=' : 'INPLACE_XOR', 813 '|=' : 'INPLACE_OR', 814 } 815 816 def visitAugName(self, node, mode): 817 if mode == "load": 818 self.loadName(node.name) 819 elif mode == "store": 820 self.storeName(node.name) 821 822 def visitAugGetattr(self, node, mode): 823 if mode == "load": 824 self.visit(node.expr) 825 self.emit('DUP_TOP') 826 self.emit('LOAD_ATTR', self.mangle(node.attrname)) 827 elif mode == "store": 828 self.emit('ROT_TWO') 829 self.emit('STORE_ATTR', self.mangle(node.attrname)) 830 831 def visitAugSlice(self, node, mode): 832 if mode == "load": 833 self.visitSlice(node, 1) 834 elif mode == "store": 835 slice = 0 836 if node.lower: 837 slice = slice | 1 838 if node.upper: 839 slice = slice | 2 840 if slice == 0: 841 self.emit('ROT_TWO') 842 elif slice == 3: 843 self.emit('ROT_FOUR') 844 else: 845 self.emit('ROT_THREE') 846 self.emit('STORE_SLICE+%d' % slice) 847 848 def visitAugSubscript(self, node, mode): 849 if len(node.subs) > 1: 850 raise SyntaxError, "augmented assignment to tuple is not possible" 851 if mode == "load": 852 self.visitSubscript(node, 1) 853 elif mode == "store": 854 self.emit('ROT_THREE') 855 self.emit('STORE_SUBSCR') 856 857 def visitExec(self, node): 858 self.visit(node.expr) 859 if node.locals is None: 860 self.emit('LOAD_CONST', None) 861 else: 862 self.visit(node.locals) 863 if node.globals is None: 864 self.emit('DUP_TOP') 865 else: 866 self.visit(node.globals) 867 self.emit('EXEC_STMT') 868 869 def visitCallFunc(self, node): 870 pos = 0 871 kw = 0 872 self.set_lineno(node) 873 self.visit(node.node) 874 for arg in node.args: 875 self.visit(arg) 876 if isinstance(arg, ast.Keyword): 877 kw = kw + 1 878 else: 879 pos = pos + 1 880 if node.star_args is not None: 881 self.visit(node.star_args) 882 if node.dstar_args is not None: 883 self.visit(node.dstar_args) 884 have_star = node.star_args is not None 885 have_dstar = node.dstar_args is not None 886 opcode = callfunc_opcode_info[have_star, have_dstar] 887 self.emit(opcode, kw << 8 | pos) 888 889 def visitPrint(self, node, newline=0): 890 self.set_lineno(node) 891 if node.dest: 892 self.visit(node.dest) 893 for child in node.nodes: 894 if node.dest: 895 self.emit('DUP_TOP') 896 self.visit(child) 897 if node.dest: 898 self.emit('ROT_TWO') 899 self.emit('PRINT_ITEM_TO') 900 else: 901 self.emit('PRINT_ITEM') 902 if node.dest and not newline: 903 self.emit('POP_TOP') 904 905 def visitPrintnl(self, node): 906 self.visitPrint(node, newline=1) 907 if node.dest: 908 self.emit('PRINT_NEWLINE_TO') 909 else: 910 self.emit('PRINT_NEWLINE') 911 912 def visitReturn(self, node): 913 self.set_lineno(node) 914 self.visit(node.value) 915 self.emit('RETURN_VALUE') 916 917 def visitYield(self, node): 918 self.set_lineno(node) 919 self.visit(node.value) 920 self.emit('YIELD_STMT') 921 922 # slice and subscript stuff 923 924 def visitSlice(self, node, aug_flag=None): 925 # aug_flag is used by visitAugSlice 926 self.visit(node.expr) 927 slice = 0 928 if node.lower: 929 self.visit(node.lower) 930 slice = slice | 1 931 if node.upper: 932 self.visit(node.upper) 933 slice = slice | 2 934 if aug_flag: 935 if slice == 0: 936 self.emit('DUP_TOP') 937 elif slice == 3: 938 self.emit('DUP_TOPX', 3) 939 else: 940 self.emit('DUP_TOPX', 2) 941 if node.flags == 'OP_APPLY': 942 self.emit('SLICE+%d' % slice) 943 elif node.flags == 'OP_ASSIGN': 944 self.emit('STORE_SLICE+%d' % slice) 945 elif node.flags == 'OP_DELETE': 946 self.emit('DELETE_SLICE+%d' % slice) 947 else: 948 print "weird slice", node.flags 949 raise 950 951 def visitSubscript(self, node, aug_flag=None): 952 self.visit(node.expr) 953 for sub in node.subs: 954 self.visit(sub) 955 if aug_flag: 956 self.emit('DUP_TOPX', 2) 957 if len(node.subs) > 1: 958 self.emit('BUILD_TUPLE', len(node.subs)) 959 if node.flags == 'OP_APPLY': 960 self.emit('BINARY_SUBSCR') 961 elif node.flags == 'OP_ASSIGN': 962 self.emit('STORE_SUBSCR') 963 elif node.flags == 'OP_DELETE': 964 self.emit('DELETE_SUBSCR') 965 966 # binary ops 967 968 def binaryOp(self, node, op): 969 self.visit(node.left) 970 self.visit(node.right) 971 self.emit(op) 972 973 def visitAdd(self, node): 974 return self.binaryOp(node, 'BINARY_ADD') 975 976 def visitSub(self, node): 977 return self.binaryOp(node, 'BINARY_SUBTRACT') 978 979 def visitMul(self, node): 980 return self.binaryOp(node, 'BINARY_MULTIPLY') 981 982 def visitDiv(self, node): 983 return self.binaryOp(node, self._div_op) 984 985 def visitFloorDiv(self, node): 986 return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE') 987 988 def visitMod(self, node): 989 return self.binaryOp(node, 'BINARY_MODULO') 990 991 def visitPower(self, node): 992 return self.binaryOp(node, 'BINARY_POWER') 993 994 def visitLeftShift(self, node): 995 return self.binaryOp(node, 'BINARY_LSHIFT') 996 997 def visitRightShift(self, node): 998 return self.binaryOp(node, 'BINARY_RSHIFT') 999 1000 # unary ops 1001 1002 def unaryOp(self, node, op): 1003 self.visit(node.expr) 1004 self.emit(op) 1005 1006 def visitInvert(self, node): 1007 return self.unaryOp(node, 'UNARY_INVERT') 1008 1009 def visitUnarySub(self, node): 1010 return self.unaryOp(node, 'UNARY_NEGATIVE') 1011 1012 def visitUnaryAdd(self, node): 1013 return self.unaryOp(node, 'UNARY_POSITIVE') 1014 1015 def visitUnaryInvert(self, node): 1016 return self.unaryOp(node, 'UNARY_INVERT') 1017 1018 def visitNot(self, node): 1019 return self.unaryOp(node, 'UNARY_NOT') 1020 1021 def visitBackquote(self, node): 1022 return self.unaryOp(node, 'UNARY_CONVERT') 1023 1024 # bit ops 1025 1026 def bitOp(self, nodes, op): 1027 self.visit(nodes[0]) 1028 for node in nodes[1:]: 1029 self.visit(node) 1030 self.emit(op) 1031 1032 def visitBitand(self, node): 1033 return self.bitOp(node.nodes, 'BINARY_AND') 1034 1035 def visitBitor(self, node): 1036 return self.bitOp(node.nodes, 'BINARY_OR') 1037 1038 def visitBitxor(self, node): 1039 return self.bitOp(node.nodes, 'BINARY_XOR') 1040 1041 # object constructors 1042 1043 def visitEllipsis(self, node): 1044 self.emit('LOAD_CONST', Ellipsis) 1045 1046 def visitTuple(self, node): 1047 self.set_lineno(node) 1048 for elt in node.nodes: 1049 self.visit(elt) 1050 self.emit('BUILD_TUPLE', len(node.nodes)) 1051 1052 def visitList(self, node): 1053 self.set_lineno(node) 1054 for elt in node.nodes: 1055 self.visit(elt) 1056 self.emit('BUILD_LIST', len(node.nodes)) 1057 1058 def visitSliceobj(self, node): 1059 for child in node.nodes: 1060 self.visit(child) 1061 self.emit('BUILD_SLICE', len(node.nodes)) 1062 1063 def visitDict(self, node): 1064 lineno = getattr(node, 'lineno', None) 1065 if lineno: 1066 self.emit('SET_LINENO', lineno) 1067 self.emit('BUILD_MAP', 0) 1068 for k, v in node.items: 1069 lineno2 = getattr(node, 'lineno', None) 1070 if lineno2 is not None and lineno != lineno2: 1071 self.emit('SET_LINENO', lineno2) 1072 lineno = lineno2 1073 self.emit('DUP_TOP') 1074 self.visit(v) 1075 self.emit('ROT_TWO') 1076 self.visit(k) 1077 self.emit('STORE_SUBSCR') 1078 1079class NestedScopeMixin: 1080 """Defines initClass() for nested scoping (Python 2.2-compatible)""" 1081 def initClass(self): 1082 self.__class__.NameFinder = LocalNameFinder 1083 self.__class__.FunctionGen = FunctionCodeGenerator 1084 self.__class__.ClassGen = ClassCodeGenerator 1085 1086class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): 1087 __super_init = CodeGenerator.__init__ 1088 1089 scopes = None 1090 1091 def __init__(self, tree): 1092 self.graph = pyassem.PyFlowGraph("<module>", tree.filename) 1093 self.futures = future.find_futures(tree) 1094 self.__super_init() 1095 walk(tree, self) 1096 1097 def get_module(self): 1098 return self 1099 1100class AbstractFunctionCode: 1101 optimized = 1 1102 lambdaCount = 0 1103 1104 def __init__(self, func, scopes, isLambda, class_name, mod): 1105 self.class_name = class_name 1106 self.module = mod 1107 if isLambda: 1108 klass = FunctionCodeGenerator 1109 name = "<lambda.%d>" % klass.lambdaCount 1110 klass.lambdaCount = klass.lambdaCount + 1 1111 else: 1112 name = func.name 1113 args, hasTupleArg = generateArgList(func.argnames) 1114 self.graph = pyassem.PyFlowGraph(name, func.filename, args, 1115 optimized=1) 1116 self.isLambda = isLambda 1117 self.super_init() 1118 1119 if not isLambda and func.doc: 1120 self.setDocstring(func.doc) 1121 1122 lnf = walk(func.code, self.NameFinder(args), verbose=0) 1123 self.locals.push(lnf.getLocals()) 1124 if func.varargs: 1125 self.graph.setFlag(CO_VARARGS) 1126 if func.kwargs: 1127 self.graph.setFlag(CO_VARKEYWORDS) 1128 self.set_lineno(func) 1129 if hasTupleArg: 1130 self.generateArgUnpack(func.argnames) 1131 1132 def get_module(self): 1133 return self.module 1134 1135 def finish(self): 1136 self.graph.startExitBlock() 1137 if not self.isLambda: 1138 self.emit('LOAD_CONST', None) 1139 self.emit('RETURN_VALUE') 1140 1141 def generateArgUnpack(self, args): 1142 for i in range(len(args)): 1143 arg = args[i] 1144 if type(arg) == types.TupleType: 1145 self.emit('LOAD_FAST', '.%d' % (i * 2)) 1146 self.unpackSequence(arg) 1147 1148 def unpackSequence(self, tup): 1149 if VERSION > 1: 1150 self.emit('UNPACK_SEQUENCE', len(tup)) 1151 else: 1152 self.emit('UNPACK_TUPLE', len(tup)) 1153 for elt in tup: 1154 if type(elt) == types.TupleType: 1155 self.unpackSequence(elt) 1156 else: 1157 self._nameOp('STORE', elt) 1158 1159 unpackTuple = unpackSequence 1160 1161class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode, 1162 CodeGenerator): 1163 super_init = CodeGenerator.__init__ # call be other init 1164 scopes = None 1165 1166 __super_init = AbstractFunctionCode.__init__ 1167 1168 def __init__(self, func, scopes, isLambda, class_name, mod): 1169 self.scopes = scopes 1170 self.scope = scopes[func] 1171 self.__super_init(func, scopes, isLambda, class_name, mod) 1172 self.graph.setFreeVars(self.scope.get_free_vars()) 1173 self.graph.setCellVars(self.scope.get_cell_vars()) 1174 if self.graph.checkFlag(CO_GENERATOR_ALLOWED): 1175 if self.scope.generator is not None: 1176 self.graph.setFlag(CO_GENERATOR) 1177 1178class AbstractClassCode: 1179 1180 def __init__(self, klass, scopes, module): 1181 self.class_name = klass.name 1182 self.module = module 1183 self.graph = pyassem.PyFlowGraph(klass.name, klass.filename, 1184 optimized=0, klass=1) 1185 self.super_init() 1186 lnf = walk(klass.code, self.NameFinder(), verbose=0) 1187 self.locals.push(lnf.getLocals()) 1188 self.graph.setFlag(CO_NEWLOCALS) 1189 if klass.doc: 1190 self.setDocstring(klass.doc) 1191 1192 def get_module(self): 1193 return self.module 1194 1195 def finish(self): 1196 self.graph.startExitBlock() 1197 self.emit('LOAD_LOCALS') 1198 self.emit('RETURN_VALUE') 1199 1200class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator): 1201 super_init = CodeGenerator.__init__ 1202 scopes = None 1203 1204 __super_init = AbstractClassCode.__init__ 1205 1206 def __init__(self, klass, scopes, module): 1207 self.scopes = scopes 1208 self.scope = scopes[klass] 1209 self.__super_init(klass, scopes, module) 1210 self.graph.setFreeVars(self.scope.get_free_vars()) 1211 self.graph.setCellVars(self.scope.get_cell_vars()) 1212## self.graph.setFlag(CO_NESTED) 1213 1214def generateArgList(arglist): 1215 """Generate an arg list marking TupleArgs""" 1216 args = [] 1217 extra = [] 1218 count = 0 1219 for i in range(len(arglist)): 1220 elt = arglist[i] 1221 if type(elt) == types.StringType: 1222 args.append(elt) 1223 elif type(elt) == types.TupleType: 1224 args.append(TupleArg(i * 2, elt)) 1225 extra.extend(misc.flatten(elt)) 1226 count = count + 1 1227 else: 1228 raise ValueError, "unexpect argument type:", elt 1229 return args + extra, count 1230 1231def findOp(node): 1232 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree""" 1233 v = OpFinder() 1234 walk(node, v, verbose=0) 1235 return v.op 1236 1237class OpFinder: 1238 def __init__(self): 1239 self.op = None 1240 def visitAssName(self, node): 1241 if self.op is None: 1242 self.op = node.flags 1243 elif self.op != node.flags: 1244 raise ValueError, "mixed ops in stmt" 1245 visitAssAttr = visitAssName 1246 1247class Delegator: 1248 """Base class to support delegation for augmented assignment nodes 1249 1250 To generator code for augmented assignments, we use the following 1251 wrapper classes. In visitAugAssign, the left-hand expression node 1252 is visited twice. The first time the visit uses the normal method 1253 for that node . The second time the visit uses a different method 1254 that generates the appropriate code to perform the assignment. 1255 These delegator classes wrap the original AST nodes in order to 1256 support the variant visit methods. 1257 """ 1258 def __init__(self, obj): 1259 self.obj = obj 1260 1261 def __getattr__(self, attr): 1262 return getattr(self.obj, attr) 1263 1264class AugGetattr(Delegator): 1265 pass 1266 1267class AugName(Delegator): 1268 pass 1269 1270class AugSlice(Delegator): 1271 pass 1272 1273class AugSubscript(Delegator): 1274 pass 1275 1276wrapper = { 1277 ast.Getattr: AugGetattr, 1278 ast.Name: AugName, 1279 ast.Slice: AugSlice, 1280 ast.Subscript: AugSubscript, 1281 } 1282 1283def wrap_aug(node): 1284 return wrapper[node.__class__](node) 1285 1286if __name__ == "__main__": 1287 import sys 1288 1289 for file in sys.argv[1:]: 1290 compile(file) 1291