diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-15 23:28:02 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-15 23:38:24 +0300 |
commit | 6635e51dd2fc4c8fbc3e510cd31c9c9102b7cfda (patch) | |
tree | d51d048d3eb5d7a24ec6e4302b589513a3b97de3 /contrib/tools/python/src/Lib/compiler/pycodegen.py | |
parent | e894914751eea93bb23102a533c8d481b80c2560 (diff) | |
download | ydb-6635e51dd2fc4c8fbc3e510cd31c9c9102b7cfda.tar.gz |
Store generated list of frozen modules for contrib/tools/python
a94357bf8071b16879eeabac1f52f54e278c03ed
Diffstat (limited to 'contrib/tools/python/src/Lib/compiler/pycodegen.py')
-rw-r--r-- | contrib/tools/python/src/Lib/compiler/pycodegen.py | 1555 |
1 files changed, 0 insertions, 1555 deletions
diff --git a/contrib/tools/python/src/Lib/compiler/pycodegen.py b/contrib/tools/python/src/Lib/compiler/pycodegen.py deleted file mode 100644 index 6515945f39a..00000000000 --- a/contrib/tools/python/src/Lib/compiler/pycodegen.py +++ /dev/null @@ -1,1555 +0,0 @@ -import imp -import os -import marshal -import struct -import sys -from cStringIO import StringIO - -from compiler import ast, parse, walk, syntax -from compiler import pyassem, misc, future, symbols -from compiler.consts import SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICIT, \ - SC_FREE, SC_CELL -from compiler.consts import (CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS, - CO_NESTED, CO_GENERATOR, CO_FUTURE_DIVISION, - CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT, CO_FUTURE_PRINT_FUNCTION) -from compiler.pyassem import TupleArg - -# XXX The version-specific code can go, since this code only works with 2.x. -# Do we have Python 1.x or Python 2.x? -try: - VERSION = sys.version_info[0] -except AttributeError: - VERSION = 1 - -callfunc_opcode_info = { - # (Have *args, Have **args) : opcode - (0,0) : "CALL_FUNCTION", - (1,0) : "CALL_FUNCTION_VAR", - (0,1) : "CALL_FUNCTION_KW", - (1,1) : "CALL_FUNCTION_VAR_KW", -} - -LOOP = 1 -EXCEPT = 2 -TRY_FINALLY = 3 -END_FINALLY = 4 - -def compileFile(filename, display=0): - f = open(filename, 'U') - buf = f.read() - f.close() - mod = Module(buf, filename) - try: - mod.compile(display) - except SyntaxError: - raise - else: - f = open(filename + "c", "wb") - mod.dump(f) - f.close() - -def compile(source, filename, mode, flags=None, dont_inherit=None): - """Replacement for builtin compile() function""" - if flags is not None or dont_inherit is not None: - raise RuntimeError, "not implemented yet" - - if mode == "single": - gen = Interactive(source, filename) - elif mode == "exec": - gen = Module(source, filename) - elif mode == "eval": - gen = Expression(source, filename) - else: - raise ValueError("compile() 3rd arg must be 'exec' or " - "'eval' or 'single'") - gen.compile() - return gen.code - -class AbstractCompileMode: - - mode = None # defined by subclass - - def __init__(self, source, filename): - self.source = source - self.filename = filename - self.code = None - - def _get_tree(self): - tree = parse(self.source, self.mode) - misc.set_filename(self.filename, tree) - syntax.check(tree) - return tree - - def compile(self): - pass # implemented by subclass - - def getCode(self): - return self.code - -class Expression(AbstractCompileMode): - - mode = "eval" - - def compile(self): - tree = self._get_tree() - gen = ExpressionCodeGenerator(tree) - self.code = gen.getCode() - -class Interactive(AbstractCompileMode): - - mode = "single" - - def compile(self): - tree = self._get_tree() - gen = InteractiveCodeGenerator(tree) - self.code = gen.getCode() - -class Module(AbstractCompileMode): - - mode = "exec" - - def compile(self, display=0): - tree = self._get_tree() - gen = ModuleCodeGenerator(tree) - if display: - import pprint - print pprint.pprint(tree) - self.code = gen.getCode() - - def dump(self, f): - f.write(self.getPycHeader()) - marshal.dump(self.code, f) - - MAGIC = imp.get_magic() - - def getPycHeader(self): - # compile.c uses marshal to write a long directly, with - # calling the interface that would also generate a 1-byte code - # to indicate the type of the value. simplest way to get the - # same effect is to call marshal and then skip the code. - mtime = os.path.getmtime(self.filename) - mtime = struct.pack('<i', mtime) - return self.MAGIC + mtime - -class LocalNameFinder: - """Find local names in scope""" - def __init__(self, names=()): - self.names = misc.Set() - self.globals = misc.Set() - for name in names: - self.names.add(name) - - # XXX list comprehensions and for loops - - def getLocals(self): - for elt in self.globals.elements(): - if self.names.has_elt(elt): - self.names.remove(elt) - return self.names - - def visitDict(self, node): - pass - - def visitGlobal(self, node): - for name in node.names: - self.globals.add(name) - - def visitFunction(self, node): - self.names.add(node.name) - - def visitLambda(self, node): - pass - - def visitImport(self, node): - for name, alias in node.names: - self.names.add(alias or name) - - def visitFrom(self, node): - for name, alias in node.names: - self.names.add(alias or name) - - def visitClass(self, node): - self.names.add(node.name) - - def visitAssName(self, node): - self.names.add(node.name) - -def is_constant_false(node): - if isinstance(node, ast.Const): - if not node.value: - return 1 - return 0 - -class CodeGenerator: - """Defines basic code generator for Python bytecode - - This class is an abstract base class. Concrete subclasses must - define an __init__() that defines self.graph and then calls the - __init__() defined in this class. - - The concrete class must also define the class attributes - NameFinder, FunctionGen, and ClassGen. These attributes can be - defined in the initClass() method, which is a hook for - initializing these methods after all the classes have been - defined. - """ - - optimized = 0 # is namespace access optimized? - __initialized = None - class_name = None # provide default for instance variable - - def __init__(self): - if self.__initialized is None: - self.initClass() - self.__class__.__initialized = 1 - self.checkClass() - self.locals = misc.Stack() - self.setups = misc.Stack() - self.last_lineno = None - self._setupGraphDelegation() - self._div_op = "BINARY_DIVIDE" - - # XXX set flags based on future features - futures = self.get_module().futures - for feature in futures: - if feature == "division": - self.graph.setFlag(CO_FUTURE_DIVISION) - self._div_op = "BINARY_TRUE_DIVIDE" - elif feature == "absolute_import": - self.graph.setFlag(CO_FUTURE_ABSIMPORT) - elif feature == "with_statement": - self.graph.setFlag(CO_FUTURE_WITH_STATEMENT) - elif feature == "print_function": - self.graph.setFlag(CO_FUTURE_PRINT_FUNCTION) - - def initClass(self): - """This method is called once for each class""" - - def checkClass(self): - """Verify that class is constructed correctly""" - try: - assert hasattr(self, 'graph') - assert getattr(self, 'NameFinder') - assert getattr(self, 'FunctionGen') - assert getattr(self, 'ClassGen') - except AssertionError, msg: - intro = "Bad class construction for %s" % self.__class__.__name__ - raise AssertionError, intro - - def _setupGraphDelegation(self): - self.emit = self.graph.emit - self.newBlock = self.graph.newBlock - self.startBlock = self.graph.startBlock - self.nextBlock = self.graph.nextBlock - self.setDocstring = self.graph.setDocstring - - def getCode(self): - """Return a code object""" - return self.graph.getCode() - - def mangle(self, name): - if self.class_name is not None: - return misc.mangle(name, self.class_name) - else: - return name - - def parseSymbols(self, tree): - s = symbols.SymbolVisitor() - walk(tree, s) - return s.scopes - - def get_module(self): - raise RuntimeError, "should be implemented by subclasses" - - # Next five methods handle name access - - def isLocalName(self, name): - return self.locals.top().has_elt(name) - - def storeName(self, name): - self._nameOp('STORE', name) - - def loadName(self, name): - self._nameOp('LOAD', name) - - def delName(self, name): - self._nameOp('DELETE', name) - - def _nameOp(self, prefix, name): - name = self.mangle(name) - scope = self.scope.check_name(name) - if scope == SC_LOCAL: - if not self.optimized: - self.emit(prefix + '_NAME', name) - else: - self.emit(prefix + '_FAST', name) - elif scope == SC_GLOBAL_EXPLICIT: - self.emit(prefix + '_GLOBAL', name) - elif scope == SC_GLOBAL_IMPLICIT: - if not self.optimized: - self.emit(prefix + '_NAME', name) - else: - self.emit(prefix + '_GLOBAL', name) - elif scope == SC_FREE or scope == SC_CELL: - self.emit(prefix + '_DEREF', name) - else: - raise RuntimeError, "unsupported scope for var %s: %d" % \ - (name, scope) - - def _implicitNameOp(self, prefix, name): - """Emit name ops for names generated implicitly by for loops - - The interpreter generates names that start with a period or - dollar sign. The symbol table ignores these names because - they aren't present in the program text. - """ - if self.optimized: - self.emit(prefix + '_FAST', name) - else: - self.emit(prefix + '_NAME', name) - - # The set_lineno() function and the explicit emit() calls for - # SET_LINENO below are only used to generate the line number table. - # As of Python 2.3, the interpreter does not have a SET_LINENO - # instruction. pyassem treats SET_LINENO opcodes as a special case. - - def set_lineno(self, node, force=False): - """Emit SET_LINENO if necessary. - - The instruction is considered necessary if the node has a - lineno attribute and it is different than the last lineno - emitted. - - Returns true if SET_LINENO was emitted. - - There are no rules for when an AST node should have a lineno - attribute. The transformer and AST code need to be reviewed - and a consistent policy implemented and documented. Until - then, this method works around missing line numbers. - """ - lineno = getattr(node, 'lineno', None) - if lineno is not None and (lineno != self.last_lineno - or force): - self.emit('SET_LINENO', lineno) - self.last_lineno = lineno - return True - return False - - # The first few visitor methods handle nodes that generator new - # code objects. They use class attributes to determine what - # specialized code generators to use. - - NameFinder = LocalNameFinder - FunctionGen = None - ClassGen = None - - def visitModule(self, node): - self.scopes = self.parseSymbols(node) - self.scope = self.scopes[node] - self.emit('SET_LINENO', 0) - if node.doc: - self.emit('LOAD_CONST', node.doc) - self.storeName('__doc__') - lnf = walk(node.node, self.NameFinder(), verbose=0) - self.locals.push(lnf.getLocals()) - self.visit(node.node) - self.emit('LOAD_CONST', None) - self.emit('RETURN_VALUE') - - def visitExpression(self, node): - self.set_lineno(node) - self.scopes = self.parseSymbols(node) - self.scope = self.scopes[node] - self.visit(node.node) - self.emit('RETURN_VALUE') - - def visitFunction(self, node): - self._visitFuncOrLambda(node, isLambda=0) - if node.doc: - self.setDocstring(node.doc) - self.storeName(node.name) - - def visitLambda(self, node): - self._visitFuncOrLambda(node, isLambda=1) - - def _visitFuncOrLambda(self, node, isLambda=0): - if not isLambda and node.decorators: - for decorator in node.decorators.nodes: - self.visit(decorator) - ndecorators = len(node.decorators.nodes) - else: - ndecorators = 0 - - gen = self.FunctionGen(node, self.scopes, isLambda, - self.class_name, self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - for default in node.defaults: - self.visit(default) - self._makeClosure(gen, len(node.defaults)) - for i in range(ndecorators): - self.emit('CALL_FUNCTION', 1) - - def visitClass(self, node): - gen = self.ClassGen(node, self.scopes, - self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - self.emit('LOAD_CONST', node.name) - for base in node.bases: - self.visit(base) - self.emit('BUILD_TUPLE', len(node.bases)) - self._makeClosure(gen, 0) - self.emit('CALL_FUNCTION', 0) - self.emit('BUILD_CLASS') - self.storeName(node.name) - - # The rest are standard visitor methods - - # The next few implement control-flow statements - - def visitIf(self, node): - end = self.newBlock() - numtests = len(node.tests) - for i in range(numtests): - test, suite = node.tests[i] - if is_constant_false(test): - # XXX will need to check generator stuff here - continue - self.set_lineno(test) - self.visit(test) - nextTest = self.newBlock() - self.emit('POP_JUMP_IF_FALSE', nextTest) - self.nextBlock() - self.visit(suite) - self.emit('JUMP_FORWARD', end) - self.startBlock(nextTest) - if node.else_: - self.visit(node.else_) - self.nextBlock(end) - - def visitWhile(self, node): - self.set_lineno(node) - - loop = self.newBlock() - else_ = self.newBlock() - - after = self.newBlock() - self.emit('SETUP_LOOP', after) - - self.nextBlock(loop) - self.setups.push((LOOP, loop)) - - self.set_lineno(node, force=True) - self.visit(node.test) - self.emit('POP_JUMP_IF_FALSE', else_ or after) - - self.nextBlock() - self.visit(node.body) - self.emit('JUMP_ABSOLUTE', loop) - - self.startBlock(else_) # or just the POPs if not else clause - self.emit('POP_BLOCK') - self.setups.pop() - if node.else_: - self.visit(node.else_) - self.nextBlock(after) - - def visitFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - after = self.newBlock() - self.setups.push((LOOP, start)) - - self.set_lineno(node) - self.emit('SETUP_LOOP', after) - self.visit(node.list) - self.emit('GET_ITER') - - self.nextBlock(start) - self.set_lineno(node, force=1) - self.emit('FOR_ITER', anchor) - self.visit(node.assign) - self.visit(node.body) - self.emit('JUMP_ABSOLUTE', start) - self.nextBlock(anchor) - self.emit('POP_BLOCK') - self.setups.pop() - if node.else_: - self.visit(node.else_) - self.nextBlock(after) - - def visitBreak(self, node): - if not self.setups: - raise SyntaxError, "'break' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - self.set_lineno(node) - self.emit('BREAK_LOOP') - - def visitContinue(self, node): - if not self.setups: - raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - kind, block = self.setups.top() - if kind == LOOP: - self.set_lineno(node) - self.emit('JUMP_ABSOLUTE', block) - self.nextBlock() - elif kind == EXCEPT or kind == TRY_FINALLY: - self.set_lineno(node) - # find the block that starts the loop - top = len(self.setups) - while top > 0: - top = top - 1 - kind, loop_block = self.setups[top] - if kind == LOOP: - break - if kind != LOOP: - raise SyntaxError, "'continue' outside loop (%s, %d)" % \ - (node.filename, node.lineno) - self.emit('CONTINUE_LOOP', loop_block) - self.nextBlock() - elif kind == END_FINALLY: - msg = "'continue' not allowed inside 'finally' clause (%s, %d)" - raise SyntaxError, msg % (node.filename, node.lineno) - - def visitTest(self, node, jump): - end = self.newBlock() - for child in node.nodes[:-1]: - self.visit(child) - self.emit(jump, end) - self.nextBlock() - self.visit(node.nodes[-1]) - self.nextBlock(end) - - def visitAnd(self, node): - self.visitTest(node, 'JUMP_IF_FALSE_OR_POP') - - def visitOr(self, node): - self.visitTest(node, 'JUMP_IF_TRUE_OR_POP') - - def visitIfExp(self, node): - endblock = self.newBlock() - elseblock = self.newBlock() - self.visit(node.test) - self.emit('POP_JUMP_IF_FALSE', elseblock) - self.visit(node.then) - self.emit('JUMP_FORWARD', endblock) - self.nextBlock(elseblock) - self.visit(node.else_) - self.nextBlock(endblock) - - def visitCompare(self, node): - self.visit(node.expr) - cleanup = self.newBlock() - for op, code in node.ops[:-1]: - self.visit(code) - self.emit('DUP_TOP') - self.emit('ROT_THREE') - self.emit('COMPARE_OP', op) - self.emit('JUMP_IF_FALSE_OR_POP', cleanup) - self.nextBlock() - # now do the last comparison - if node.ops: - op, code = node.ops[-1] - self.visit(code) - self.emit('COMPARE_OP', op) - if len(node.ops) > 1: - end = self.newBlock() - self.emit('JUMP_FORWARD', end) - self.startBlock(cleanup) - self.emit('ROT_TWO') - self.emit('POP_TOP') - self.nextBlock(end) - - # list comprehensions - def visitListComp(self, node): - self.set_lineno(node) - # setup list - self.emit('BUILD_LIST', 0) - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor)) - - self.visit(node.expr) - self.emit('LIST_APPEND', len(node.quals) + 1) - - for start, cont, anchor in stack: - if cont: - self.nextBlock(cont) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - - def visitSetComp(self, node): - self.set_lineno(node) - # setup list - self.emit('BUILD_SET', 0) - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor)) - - self.visit(node.expr) - self.emit('SET_ADD', len(node.quals) + 1) - - for start, cont, anchor in stack: - if cont: - self.nextBlock(cont) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - - def visitDictComp(self, node): - self.set_lineno(node) - # setup list - self.emit('BUILD_MAP', 0) - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor)) - - self.visit(node.value) - self.visit(node.key) - self.emit('MAP_ADD', len(node.quals) + 1) - - for start, cont, anchor in stack: - if cont: - self.nextBlock(cont) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - - def visitListCompFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - - self.visit(node.list) - self.emit('GET_ITER') - self.nextBlock(start) - self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() - self.visit(node.assign) - return start, anchor - - def visitListCompIf(self, node, branch): - self.set_lineno(node, force=True) - self.visit(node.test) - self.emit('POP_JUMP_IF_FALSE', branch) - self.newBlock() - - def _makeClosure(self, gen, args): - frees = gen.scope.get_free_vars() - if frees: - for name in frees: - self.emit('LOAD_CLOSURE', name) - self.emit('BUILD_TUPLE', len(frees)) - self.emit('LOAD_CONST', gen) - self.emit('MAKE_CLOSURE', args) - else: - self.emit('LOAD_CONST', gen) - self.emit('MAKE_FUNCTION', args) - - def visitGenExpr(self, node): - gen = GenExprCodeGenerator(node, self.scopes, self.class_name, - self.get_module()) - walk(node.code, gen) - gen.finish() - self.set_lineno(node) - self._makeClosure(gen, 0) - # precomputation of outmost iterable - self.visit(node.code.quals[0].iter) - self.emit('GET_ITER') - self.emit('CALL_FUNCTION', 1) - - def visitGenExprInner(self, node): - self.set_lineno(node) - # setup list - - stack = [] - for i, for_ in zip(range(len(node.quals)), node.quals): - start, anchor, end = self.visit(for_) - cont = None - for if_ in for_.ifs: - if cont is None: - cont = self.newBlock() - self.visit(if_, cont) - stack.insert(0, (start, cont, anchor, end)) - - self.visit(node.expr) - self.emit('YIELD_VALUE') - self.emit('POP_TOP') - - for start, cont, anchor, end in stack: - if cont: - self.nextBlock(cont) - self.emit('JUMP_ABSOLUTE', start) - self.startBlock(anchor) - self.emit('POP_BLOCK') - self.setups.pop() - self.nextBlock(end) - - self.emit('LOAD_CONST', None) - - def visitGenExprFor(self, node): - start = self.newBlock() - anchor = self.newBlock() - end = self.newBlock() - - self.setups.push((LOOP, start)) - self.emit('SETUP_LOOP', end) - - if node.is_outmost: - self.loadName('.0') - else: - self.visit(node.iter) - self.emit('GET_ITER') - - self.nextBlock(start) - self.set_lineno(node, force=True) - self.emit('FOR_ITER', anchor) - self.nextBlock() - self.visit(node.assign) - return start, anchor, end - - def visitGenExprIf(self, node, branch): - self.set_lineno(node, force=True) - self.visit(node.test) - self.emit('POP_JUMP_IF_FALSE', branch) - self.newBlock() - - # exception related - - def visitAssert(self, node): - # XXX would be interesting to implement this via a - # transformation of the AST before this stage - if __debug__: - end = self.newBlock() - self.set_lineno(node) - # XXX AssertionError appears to be special case -- it is always - # loaded as a global even if there is a local name. I guess this - # is a sort of renaming op. - self.nextBlock() - self.visit(node.test) - self.emit('POP_JUMP_IF_TRUE', end) - self.nextBlock() - self.emit('LOAD_GLOBAL', 'AssertionError') - if node.fail: - self.visit(node.fail) - self.emit('RAISE_VARARGS', 2) - else: - self.emit('RAISE_VARARGS', 1) - self.nextBlock(end) - - def visitRaise(self, node): - self.set_lineno(node) - n = 0 - if node.expr1: - self.visit(node.expr1) - n = n + 1 - if node.expr2: - self.visit(node.expr2) - n = n + 1 - if node.expr3: - self.visit(node.expr3) - n = n + 1 - self.emit('RAISE_VARARGS', n) - - def visitTryExcept(self, node): - body = self.newBlock() - handlers = self.newBlock() - end = self.newBlock() - if node.else_: - lElse = self.newBlock() - else: - lElse = end - self.set_lineno(node) - self.emit('SETUP_EXCEPT', handlers) - self.nextBlock(body) - self.setups.push((EXCEPT, body)) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('JUMP_FORWARD', lElse) - self.startBlock(handlers) - - last = len(node.handlers) - 1 - for i in range(len(node.handlers)): - expr, target, body = node.handlers[i] - self.set_lineno(expr) - if expr: - self.emit('DUP_TOP') - self.visit(expr) - self.emit('COMPARE_OP', 'exception match') - next = self.newBlock() - self.emit('POP_JUMP_IF_FALSE', next) - self.nextBlock() - self.emit('POP_TOP') - if target: - self.visit(target) - else: - self.emit('POP_TOP') - self.emit('POP_TOP') - self.visit(body) - self.emit('JUMP_FORWARD', end) - if expr: - self.nextBlock(next) - else: - self.nextBlock() - self.emit('END_FINALLY') - if node.else_: - self.nextBlock(lElse) - self.visit(node.else_) - self.nextBlock(end) - - def visitTryFinally(self, node): - body = self.newBlock() - final = self.newBlock() - self.set_lineno(node) - self.emit('SETUP_FINALLY', final) - self.nextBlock(body) - self.setups.push((TRY_FINALLY, body)) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('LOAD_CONST', None) - self.nextBlock(final) - self.setups.push((END_FINALLY, final)) - self.visit(node.final) - self.emit('END_FINALLY') - self.setups.pop() - - __with_count = 0 - - def visitWith(self, node): - body = self.newBlock() - final = self.newBlock() - self.__with_count += 1 - valuevar = "_[%d]" % self.__with_count - self.set_lineno(node) - self.visit(node.expr) - self.emit('DUP_TOP') - self.emit('LOAD_ATTR', '__exit__') - self.emit('ROT_TWO') - self.emit('LOAD_ATTR', '__enter__') - self.emit('CALL_FUNCTION', 0) - if node.vars is None: - self.emit('POP_TOP') - else: - self._implicitNameOp('STORE', valuevar) - self.emit('SETUP_FINALLY', final) - self.nextBlock(body) - self.setups.push((TRY_FINALLY, body)) - if node.vars is not None: - self._implicitNameOp('LOAD', valuevar) - self._implicitNameOp('DELETE', valuevar) - self.visit(node.vars) - self.visit(node.body) - self.emit('POP_BLOCK') - self.setups.pop() - self.emit('LOAD_CONST', None) - self.nextBlock(final) - self.setups.push((END_FINALLY, final)) - self.emit('WITH_CLEANUP') - self.emit('END_FINALLY') - self.setups.pop() - self.__with_count -= 1 - - # misc - - def visitDiscard(self, node): - self.set_lineno(node) - self.visit(node.expr) - self.emit('POP_TOP') - - def visitConst(self, node): - self.emit('LOAD_CONST', node.value) - - def visitKeyword(self, node): - self.emit('LOAD_CONST', node.name) - self.visit(node.expr) - - def visitGlobal(self, node): - # no code to generate - pass - - def visitName(self, node): - self.set_lineno(node) - self.loadName(node.name) - - def visitPass(self, node): - self.set_lineno(node) - - def visitImport(self, node): - self.set_lineno(node) - level = 0 if self.graph.checkFlag(CO_FUTURE_ABSIMPORT) else -1 - for name, alias in node.names: - if VERSION > 1: - self.emit('LOAD_CONST', level) - self.emit('LOAD_CONST', None) - self.emit('IMPORT_NAME', name) - mod = name.split(".")[0] - if alias: - self._resolveDots(name) - self.storeName(alias) - else: - self.storeName(mod) - - def visitFrom(self, node): - self.set_lineno(node) - level = node.level - if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT): - level = -1 - fromlist = tuple(name for (name, alias) in node.names) - if VERSION > 1: - self.emit('LOAD_CONST', level) - self.emit('LOAD_CONST', fromlist) - self.emit('IMPORT_NAME', node.modname) - for name, alias in node.names: - if VERSION > 1: - if name == '*': - self.namespace = 0 - self.emit('IMPORT_STAR') - # There can only be one name w/ from ... import * - assert len(node.names) == 1 - return - else: - self.emit('IMPORT_FROM', name) - self._resolveDots(name) - self.storeName(alias or name) - else: - self.emit('IMPORT_FROM', name) - self.emit('POP_TOP') - - def _resolveDots(self, name): - elts = name.split(".") - if len(elts) == 1: - return - for elt in elts[1:]: - self.emit('LOAD_ATTR', elt) - - def visitGetattr(self, node): - self.visit(node.expr) - self.emit('LOAD_ATTR', self.mangle(node.attrname)) - - # next five implement assignments - - def visitAssign(self, node): - self.set_lineno(node) - self.visit(node.expr) - dups = len(node.nodes) - 1 - for i in range(len(node.nodes)): - elt = node.nodes[i] - if i < dups: - self.emit('DUP_TOP') - if isinstance(elt, ast.Node): - self.visit(elt) - - def visitAssName(self, node): - if node.flags == 'OP_ASSIGN': - self.storeName(node.name) - elif node.flags == 'OP_DELETE': - self.set_lineno(node) - self.delName(node.name) - else: - print "oops", node.flags - - def visitAssAttr(self, node): - self.visit(node.expr) - if node.flags == 'OP_ASSIGN': - self.emit('STORE_ATTR', self.mangle(node.attrname)) - elif node.flags == 'OP_DELETE': - self.emit('DELETE_ATTR', self.mangle(node.attrname)) - else: - print "warning: unexpected flags:", node.flags - print node - - def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'): - if findOp(node) != 'OP_DELETE': - self.emit(op, len(node.nodes)) - for child in node.nodes: - self.visit(child) - - if VERSION > 1: - visitAssTuple = _visitAssSequence - visitAssList = _visitAssSequence - else: - def visitAssTuple(self, node): - self._visitAssSequence(node, 'UNPACK_TUPLE') - - def visitAssList(self, node): - self._visitAssSequence(node, 'UNPACK_LIST') - - # augmented assignment - - def visitAugAssign(self, node): - self.set_lineno(node) - aug_node = wrap_aug(node.node) - self.visit(aug_node, "load") - self.visit(node.expr) - self.emit(self._augmented_opcode[node.op]) - self.visit(aug_node, "store") - - _augmented_opcode = { - '+=' : 'INPLACE_ADD', - '-=' : 'INPLACE_SUBTRACT', - '*=' : 'INPLACE_MULTIPLY', - '/=' : 'INPLACE_DIVIDE', - '//=': 'INPLACE_FLOOR_DIVIDE', - '%=' : 'INPLACE_MODULO', - '**=': 'INPLACE_POWER', - '>>=': 'INPLACE_RSHIFT', - '<<=': 'INPLACE_LSHIFT', - '&=' : 'INPLACE_AND', - '^=' : 'INPLACE_XOR', - '|=' : 'INPLACE_OR', - } - - def visitAugName(self, node, mode): - if mode == "load": - self.loadName(node.name) - elif mode == "store": - self.storeName(node.name) - - def visitAugGetattr(self, node, mode): - if mode == "load": - self.visit(node.expr) - self.emit('DUP_TOP') - self.emit('LOAD_ATTR', self.mangle(node.attrname)) - elif mode == "store": - self.emit('ROT_TWO') - self.emit('STORE_ATTR', self.mangle(node.attrname)) - - def visitAugSlice(self, node, mode): - if mode == "load": - self.visitSlice(node, 1) - elif mode == "store": - slice = 0 - if node.lower: - slice = slice | 1 - if node.upper: - slice = slice | 2 - if slice == 0: - self.emit('ROT_TWO') - elif slice == 3: - self.emit('ROT_FOUR') - else: - self.emit('ROT_THREE') - self.emit('STORE_SLICE+%d' % slice) - - def visitAugSubscript(self, node, mode): - if mode == "load": - self.visitSubscript(node, 1) - elif mode == "store": - self.emit('ROT_THREE') - self.emit('STORE_SUBSCR') - - def visitExec(self, node): - self.visit(node.expr) - if node.locals is None: - self.emit('LOAD_CONST', None) - else: - self.visit(node.locals) - if node.globals is None: - self.emit('DUP_TOP') - else: - self.visit(node.globals) - self.emit('EXEC_STMT') - - def visitCallFunc(self, node): - pos = 0 - kw = 0 - self.set_lineno(node) - self.visit(node.node) - for arg in node.args: - self.visit(arg) - if isinstance(arg, ast.Keyword): - kw = kw + 1 - else: - pos = pos + 1 - if node.star_args is not None: - self.visit(node.star_args) - if node.dstar_args is not None: - self.visit(node.dstar_args) - have_star = node.star_args is not None - have_dstar = node.dstar_args is not None - opcode = callfunc_opcode_info[have_star, have_dstar] - self.emit(opcode, kw << 8 | pos) - - def visitPrint(self, node, newline=0): - self.set_lineno(node) - if node.dest: - self.visit(node.dest) - for child in node.nodes: - if node.dest: - self.emit('DUP_TOP') - self.visit(child) - if node.dest: - self.emit('ROT_TWO') - self.emit('PRINT_ITEM_TO') - else: - self.emit('PRINT_ITEM') - if node.dest and not newline: - self.emit('POP_TOP') - - def visitPrintnl(self, node): - self.visitPrint(node, newline=1) - if node.dest: - self.emit('PRINT_NEWLINE_TO') - else: - self.emit('PRINT_NEWLINE') - - def visitReturn(self, node): - self.set_lineno(node) - self.visit(node.value) - self.emit('RETURN_VALUE') - - def visitYield(self, node): - self.set_lineno(node) - self.visit(node.value) - self.emit('YIELD_VALUE') - - # slice and subscript stuff - - def visitSlice(self, node, aug_flag=None): - # aug_flag is used by visitAugSlice - self.visit(node.expr) - slice = 0 - if node.lower: - self.visit(node.lower) - slice = slice | 1 - if node.upper: - self.visit(node.upper) - slice = slice | 2 - if aug_flag: - if slice == 0: - self.emit('DUP_TOP') - elif slice == 3: - self.emit('DUP_TOPX', 3) - else: - self.emit('DUP_TOPX', 2) - if node.flags == 'OP_APPLY': - self.emit('SLICE+%d' % slice) - elif node.flags == 'OP_ASSIGN': - self.emit('STORE_SLICE+%d' % slice) - elif node.flags == 'OP_DELETE': - self.emit('DELETE_SLICE+%d' % slice) - else: - print "weird slice", node.flags - raise - - def visitSubscript(self, node, aug_flag=None): - self.visit(node.expr) - for sub in node.subs: - self.visit(sub) - if len(node.subs) > 1: - self.emit('BUILD_TUPLE', len(node.subs)) - if aug_flag: - self.emit('DUP_TOPX', 2) - if node.flags == 'OP_APPLY': - self.emit('BINARY_SUBSCR') - elif node.flags == 'OP_ASSIGN': - self.emit('STORE_SUBSCR') - elif node.flags == 'OP_DELETE': - self.emit('DELETE_SUBSCR') - - # binary ops - - def binaryOp(self, node, op): - self.visit(node.left) - self.visit(node.right) - self.emit(op) - - def visitAdd(self, node): - return self.binaryOp(node, 'BINARY_ADD') - - def visitSub(self, node): - return self.binaryOp(node, 'BINARY_SUBTRACT') - - def visitMul(self, node): - return self.binaryOp(node, 'BINARY_MULTIPLY') - - def visitDiv(self, node): - return self.binaryOp(node, self._div_op) - - def visitFloorDiv(self, node): - return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE') - - def visitMod(self, node): - return self.binaryOp(node, 'BINARY_MODULO') - - def visitPower(self, node): - return self.binaryOp(node, 'BINARY_POWER') - - def visitLeftShift(self, node): - return self.binaryOp(node, 'BINARY_LSHIFT') - - def visitRightShift(self, node): - return self.binaryOp(node, 'BINARY_RSHIFT') - - # unary ops - - def unaryOp(self, node, op): - self.visit(node.expr) - self.emit(op) - - def visitInvert(self, node): - return self.unaryOp(node, 'UNARY_INVERT') - - def visitUnarySub(self, node): - return self.unaryOp(node, 'UNARY_NEGATIVE') - - def visitUnaryAdd(self, node): - return self.unaryOp(node, 'UNARY_POSITIVE') - - def visitUnaryInvert(self, node): - return self.unaryOp(node, 'UNARY_INVERT') - - def visitNot(self, node): - return self.unaryOp(node, 'UNARY_NOT') - - def visitBackquote(self, node): - return self.unaryOp(node, 'UNARY_CONVERT') - - # bit ops - - def bitOp(self, nodes, op): - self.visit(nodes[0]) - for node in nodes[1:]: - self.visit(node) - self.emit(op) - - def visitBitand(self, node): - return self.bitOp(node.nodes, 'BINARY_AND') - - def visitBitor(self, node): - return self.bitOp(node.nodes, 'BINARY_OR') - - def visitBitxor(self, node): - return self.bitOp(node.nodes, 'BINARY_XOR') - - # object constructors - - def visitEllipsis(self, node): - self.emit('LOAD_CONST', Ellipsis) - - def visitTuple(self, node): - self.set_lineno(node) - for elt in node.nodes: - self.visit(elt) - self.emit('BUILD_TUPLE', len(node.nodes)) - - def visitList(self, node): - self.set_lineno(node) - for elt in node.nodes: - self.visit(elt) - self.emit('BUILD_LIST', len(node.nodes)) - - def visitSet(self, node): - self.set_lineno(node) - for elt in node.nodes: - self.visit(elt) - self.emit('BUILD_SET', len(node.nodes)) - - def visitSliceobj(self, node): - for child in node.nodes: - self.visit(child) - self.emit('BUILD_SLICE', len(node.nodes)) - - def visitDict(self, node): - self.set_lineno(node) - self.emit('BUILD_MAP', 0) - for k, v in node.items: - self.emit('DUP_TOP') - self.visit(k) - self.visit(v) - self.emit('ROT_THREE') - self.emit('STORE_SUBSCR') - -class NestedScopeMixin: - """Defines initClass() for nested scoping (Python 2.2-compatible)""" - def initClass(self): - self.__class__.NameFinder = LocalNameFinder - self.__class__.FunctionGen = FunctionCodeGenerator - self.__class__.ClassGen = ClassCodeGenerator - -class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): - __super_init = CodeGenerator.__init__ - - scopes = None - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<module>", tree.filename) - self.futures = future.find_futures(tree) - self.__super_init() - walk(tree, self) - - def get_module(self): - return self - -class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator): - __super_init = CodeGenerator.__init__ - - scopes = None - futures = () - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<expression>", tree.filename) - self.__super_init() - walk(tree, self) - - def get_module(self): - return self - -class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator): - - __super_init = CodeGenerator.__init__ - - scopes = None - futures = () - - def __init__(self, tree): - self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename) - self.__super_init() - self.set_lineno(tree) - walk(tree, self) - self.emit('RETURN_VALUE') - - def get_module(self): - return self - - def visitDiscard(self, node): - # XXX Discard means it's an expression. Perhaps this is a bad - # name. - self.visit(node.expr) - self.emit('PRINT_EXPR') - -class AbstractFunctionCode: - optimized = 1 - lambdaCount = 0 - - def __init__(self, func, scopes, isLambda, class_name, mod): - self.class_name = class_name - self.module = mod - if isLambda: - klass = FunctionCodeGenerator - name = "<lambda.%d>" % klass.lambdaCount - klass.lambdaCount = klass.lambdaCount + 1 - else: - name = func.name - - args, hasTupleArg = generateArgList(func.argnames) - self.graph = pyassem.PyFlowGraph(name, func.filename, args, - optimized=1) - self.isLambda = isLambda - self.super_init() - - if not isLambda and func.doc: - self.setDocstring(func.doc) - - lnf = walk(func.code, self.NameFinder(args), verbose=0) - self.locals.push(lnf.getLocals()) - if func.varargs: - self.graph.setFlag(CO_VARARGS) - if func.kwargs: - self.graph.setFlag(CO_VARKEYWORDS) - self.set_lineno(func) - if hasTupleArg: - self.generateArgUnpack(func.argnames) - - def get_module(self): - return self.module - - def finish(self): - self.graph.startExitBlock() - if not self.isLambda: - self.emit('LOAD_CONST', None) - self.emit('RETURN_VALUE') - - def generateArgUnpack(self, args): - for i in range(len(args)): - arg = args[i] - if isinstance(arg, tuple): - self.emit('LOAD_FAST', '.%d' % (i * 2)) - self.unpackSequence(arg) - - def unpackSequence(self, tup): - if VERSION > 1: - self.emit('UNPACK_SEQUENCE', len(tup)) - else: - self.emit('UNPACK_TUPLE', len(tup)) - for elt in tup: - if isinstance(elt, tuple): - self.unpackSequence(elt) - else: - self._nameOp('STORE', elt) - - unpackTuple = unpackSequence - -class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode, - CodeGenerator): - super_init = CodeGenerator.__init__ # call be other init - scopes = None - - __super_init = AbstractFunctionCode.__init__ - - def __init__(self, func, scopes, isLambda, class_name, mod): - self.scopes = scopes - self.scope = scopes[func] - self.__super_init(func, scopes, isLambda, class_name, mod) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - if self.scope.generator is not None: - self.graph.setFlag(CO_GENERATOR) - -class GenExprCodeGenerator(NestedScopeMixin, AbstractFunctionCode, - CodeGenerator): - super_init = CodeGenerator.__init__ # call be other init - scopes = None - - __super_init = AbstractFunctionCode.__init__ - - def __init__(self, gexp, scopes, class_name, mod): - self.scopes = scopes - self.scope = scopes[gexp] - self.__super_init(gexp, scopes, 1, class_name, mod) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - self.graph.setFlag(CO_GENERATOR) - -class AbstractClassCode: - - def __init__(self, klass, scopes, module): - self.class_name = klass.name - self.module = module - self.graph = pyassem.PyFlowGraph(klass.name, klass.filename, - optimized=0, klass=1) - self.super_init() - lnf = walk(klass.code, self.NameFinder(), verbose=0) - self.locals.push(lnf.getLocals()) - self.graph.setFlag(CO_NEWLOCALS) - if klass.doc: - self.setDocstring(klass.doc) - - def get_module(self): - return self.module - - def finish(self): - self.graph.startExitBlock() - self.emit('LOAD_LOCALS') - self.emit('RETURN_VALUE') - -class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator): - super_init = CodeGenerator.__init__ - scopes = None - - __super_init = AbstractClassCode.__init__ - - def __init__(self, klass, scopes, module): - self.scopes = scopes - self.scope = scopes[klass] - self.__super_init(klass, scopes, module) - self.graph.setFreeVars(self.scope.get_free_vars()) - self.graph.setCellVars(self.scope.get_cell_vars()) - self.set_lineno(klass) - self.emit("LOAD_GLOBAL", "__name__") - self.storeName("__module__") - if klass.doc: - self.emit("LOAD_CONST", klass.doc) - self.storeName('__doc__') - -def generateArgList(arglist): - """Generate an arg list marking TupleArgs""" - args = [] - extra = [] - count = 0 - for i in range(len(arglist)): - elt = arglist[i] - if isinstance(elt, str): - args.append(elt) - elif isinstance(elt, tuple): - args.append(TupleArg(i * 2, elt)) - extra.extend(misc.flatten(elt)) - count = count + 1 - else: - raise ValueError, "unexpect argument type:", elt - return args + extra, count - -def findOp(node): - """Find the op (DELETE, LOAD, STORE) in an AssTuple tree""" - v = OpFinder() - walk(node, v, verbose=0) - return v.op - -class OpFinder: - def __init__(self): - self.op = None - def visitAssName(self, node): - if self.op is None: - self.op = node.flags - elif self.op != node.flags: - raise ValueError, "mixed ops in stmt" - visitAssAttr = visitAssName - visitSubscript = visitAssName - -class Delegator: - """Base class to support delegation for augmented assignment nodes - - To generator code for augmented assignments, we use the following - wrapper classes. In visitAugAssign, the left-hand expression node - is visited twice. The first time the visit uses the normal method - for that node . The second time the visit uses a different method - that generates the appropriate code to perform the assignment. - These delegator classes wrap the original AST nodes in order to - support the variant visit methods. - """ - def __init__(self, obj): - self.obj = obj - - def __getattr__(self, attr): - return getattr(self.obj, attr) - -class AugGetattr(Delegator): - pass - -class AugName(Delegator): - pass - -class AugSlice(Delegator): - pass - -class AugSubscript(Delegator): - pass - -wrapper = { - ast.Getattr: AugGetattr, - ast.Name: AugName, - ast.Slice: AugSlice, - ast.Subscript: AugSubscript, - } - -def wrap_aug(node): - return wrapper[node.__class__](node) - -if __name__ == "__main__": - for file in sys.argv[1:]: - compileFile(file) |