OILS / opy / compiler2 / pycodegen.py View on Github | oilshell.org

1410 lines, 1039 significant
1import itertools
2
3from . import ast, pyassem, misc
4from .visitor import ASTVisitor
5from .consts import (
6 SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICIT, SC_FREE, SC_CELL)
7
8# NOTE: removed CO_NESTED because it is unused
9from .consts import (
10 CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,
11 CO_GENERATOR, CO_FUTURE_DIVISION,
12 CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT, CO_FUTURE_PRINT_FUNCTION)
13
14_CALLFUNC_OPCODE_INFO = {
15 # (Have *args, Have **args) : opcode
16 (0,0) : "CALL_FUNCTION",
17 (1,0) : "CALL_FUNCTION_VAR",
18 (0,1) : "CALL_FUNCTION_KW",
19 (1,1) : "CALL_FUNCTION_VAR_KW",
20}
21
22_AUGMENTED_OPCODE = {
23 '+=' : 'INPLACE_ADD',
24 '-=' : 'INPLACE_SUBTRACT',
25 '*=' : 'INPLACE_MULTIPLY',
26 '/=' : 'INPLACE_DIVIDE',
27 '//=': 'INPLACE_FLOOR_DIVIDE',
28 '%=' : 'INPLACE_MODULO',
29 '**=': 'INPLACE_POWER',
30 '>>=': 'INPLACE_RSHIFT',
31 '<<=': 'INPLACE_LSHIFT',
32 '&=' : 'INPLACE_AND',
33 '^=' : 'INPLACE_XOR',
34 '|=' : 'INPLACE_OR',
35}
36
37
38LOOP = 1
39EXCEPT = 2
40TRY_FINALLY = 3
41END_FINALLY = 4
42
43
44# TODO: Move this to _ModuleContext so it's not a mutable global?
45gLambdaCounter = itertools.count()
46
47
48class LocalNameFinder(ASTVisitor):
49 """Find local names in scope"""
50 def __init__(self, names=None):
51 ASTVisitor.__init__(self)
52 self.names = names or set()
53 self.globals = set()
54
55 # XXX list comprehensions and for loops
56
57 def getLocals(self):
58 # TODO: set difference
59 for elt in self.globals:
60 if elt in self.names:
61 self.names.remove(elt)
62 return self.names
63
64 def visitDict(self, node):
65 pass
66
67 def visitGlobal(self, node):
68 for name in node.names:
69 self.globals.add(name)
70
71 def visitFunction(self, node):
72 self.names.add(node.name)
73
74 def visitLambda(self, node):
75 pass
76
77 def visitImport(self, node):
78 for name, alias in node.names:
79 self.names.add(alias or name)
80
81 def visitFrom(self, node):
82 for name, alias in node.names:
83 self.names.add(alias or name)
84
85 def visitClass(self, node):
86 self.names.add(node.name)
87
88 def visitAssName(self, node):
89 self.names.add(node.name)
90
91
92def is_constant_false(node):
93 return isinstance(node, ast.Const) and not node.value
94
95
96class Stack(list):
97
98 def push(self, elt):
99 self.append(elt)
100
101 def top(self):
102 return self[-1]
103
104
105class CodeGenerator(ASTVisitor):
106 """Abstract class for modules, classes, functions."""
107
108 def __init__(self, ctx, frame, graph):
109 ASTVisitor.__init__(self)
110 self.ctx = ctx # passed down to child CodeGenerator instances
111 self.frame = frame
112 self.graph = graph
113
114 # Set by visitModule, visitExpression (for eval), or by subclass
115 # constructor.
116 self.scope = None
117 self.class_name = None # For name mangling; set by subclasses.
118
119 self.locals = Stack()
120 self.setups = Stack()
121 self.__with_count = 0
122 self.last_lineno = None
123 self._div_op = "BINARY_DIVIDE"
124
125 # Delegate methods to graph object
126 self.emit = self.graph.emit
127 self.newBlock = self.graph.newBlock
128 self.startBlock = self.graph.startBlock
129 self.nextBlock = self.graph.nextBlock
130
131 # Set flags based on future features
132 for feature in ctx.futures:
133 if feature == "division":
134 self.frame.setFlag(CO_FUTURE_DIVISION)
135 self._div_op = "BINARY_TRUE_DIVIDE"
136 elif feature == "absolute_import":
137 self.frame.setFlag(CO_FUTURE_ABSIMPORT)
138 elif feature == "with_statement":
139 self.frame.setFlag(CO_FUTURE_WITH_STATEMENT)
140 elif feature == "print_function":
141 self.frame.setFlag(CO_FUTURE_PRINT_FUNCTION)
142
143 #
144 # Two methods that subclasses must implement.
145 #
146
147 def _Start(self):
148 raise NotImplementedError
149
150 def Finish(self):
151 raise NotImplementedError
152
153 def Start(self):
154 self.frame.setVars(self.scope.get_free_vars(),
155 self.scope.get_cell_vars())
156 self._Start()
157
158 def _mangle(self, name):
159 return misc.mangle(name, self.class_name)
160
161 def _optimized(self):
162 """Is namespace access optimized?"""
163 return False
164
165 # Next five methods handle name access
166
167 def storeName(self, name):
168 self._nameOp('STORE', name)
169
170 def loadName(self, name):
171 self._nameOp('LOAD', name)
172
173 def delName(self, name):
174 self._nameOp('DELETE', name)
175
176 def _nameOp(self, prefix, name):
177 name = self._mangle(name)
178 scope = self.scope.check_name(name)
179
180 if scope == SC_LOCAL:
181 suffix = 'FAST' if self._optimized() else 'NAME'
182 self.emit('%s_%s' % (prefix, suffix), name)
183
184 elif scope == SC_GLOBAL_EXPLICIT:
185 self.emit(prefix + '_GLOBAL', name)
186
187 elif scope == SC_GLOBAL_IMPLICIT:
188 suffix = 'GLOBAL' if self._optimized() else 'NAME'
189 self.emit('%s_%s' % (prefix, suffix), name)
190
191 elif scope == SC_FREE or scope == SC_CELL:
192 self.emit(prefix + '_DEREF', name)
193
194 else:
195 raise RuntimeError, "unsupported scope for var %s: %d" % \
196 (name, scope)
197
198 def _implicitNameOp(self, prefix, name):
199 """Emit name ops for names generated implicitly by for loops
200
201 The interpreter generates names that start with a period or
202 dollar sign. The symbol table ignores these names because
203 they aren't present in the program text.
204 """
205 suffix = 'FAST' if self._optimized() else 'NAME'
206 self.emit('%s_%s' % (prefix, suffix), name)
207
208 # The set_lineno() function and the explicit emit() calls for
209 # SET_LINENO below are only used to generate the line number table.
210 # As of Python 2.3, the interpreter does not have a SET_LINENO
211 # instruction. pyassem treats SET_LINENO opcodes as a special case.
212
213 def set_lineno(self, node, force=False):
214 """Emit SET_LINENO if necessary.
215
216 The instruction is considered necessary if the node has a
217 lineno attribute and it is different than the last lineno
218 emitted.
219
220 Returns true if SET_LINENO was emitted.
221
222 There are no rules for when an AST node should have a lineno
223 attribute. The transformer and AST code need to be reviewed
224 and a consistent policy implemented and documented. Until
225 then, this method works around missing line numbers.
226 """
227 lineno = getattr(node, 'lineno', None)
228 if lineno is not None and (lineno != self.last_lineno or force):
229 self.emit('SET_LINENO', lineno)
230 self.last_lineno = lineno
231 return True
232 return False
233
234 def visitModule(self, node):
235 self.scope = self.ctx.scopes[node]
236 self.emit('SET_LINENO', 0)
237 if node.doc and self.ctx.comp_opt.emit_docstring:
238 self.emit('LOAD_CONST', node.doc)
239 self.storeName('__doc__')
240
241 lnf = LocalNameFinder()
242 lnf.Dispatch(node.node)
243
244 self.locals.push(lnf.getLocals())
245 self.visit(node.node)
246 self.emit('LOAD_CONST', None)
247 self.emit('RETURN_VALUE')
248
249 def visitExpression(self, node):
250 """Expression is an artificial node to support "eval".
251
252 TODO: Could be renamed EvalModule?
253 """
254 self.set_lineno(node)
255 self.scope = self.ctx.scopes[node]
256 self.visit(node.node)
257 self.emit('RETURN_VALUE')
258
259 # Differences between functions and lambdas:
260 # - lambdas need an auto-generated name for the code object
261 # - lamdbda don't have docstrings
262 # - lambdas can't have decorators
263 # - code gen: non-lambdas need an extra LOAD_CONST at the end, see Finish()
264
265 def visitFunction(self, node):
266 if node.decorators:
267 for decorator in node.decorators.nodes:
268 self.visit(decorator)
269 ndecorators = len(node.decorators.nodes)
270 else:
271 ndecorators = 0
272
273 _CheckNoTupleArgs(node)
274
275 frame = pyassem.Frame(node.name, self.ctx.filename, optimized=1)
276 frame.setArgs(node.argnames)
277 graph = pyassem.FlowGraph()
278
279 # NOTE: This is a new CodeGenerator instance because each function has
280 # its own scope.
281 gen = FunctionCodeGenerator(self.ctx, frame, graph, node,
282 self.class_name)
283
284 self._funcOrLambda(node, gen, ndecorators)
285
286 # TODO: This seems like a bug. We already setDocstring in FindLocals()
287 # on the FunctionCodeGenerator below. This seems to mean that the
288 # MODULE gets the docstring of the last function? But this changes the
289 # output.
290 if node.doc:
291 self.frame.setDocstring(node.doc)
292 self.storeName(node.name)
293
294 def visitLambda(self, node):
295 obj_name = "<lambda.%d>" % gLambdaCounter.next()
296
297 _CheckNoTupleArgs(node)
298 frame = pyassem.Frame(obj_name, self.ctx.filename, optimized=1)
299 frame.setArgs(node.argnames)
300 graph = pyassem.FlowGraph()
301
302 gen = LambdaCodeGenerator(self.ctx, frame, graph, node,
303 self.class_name)
304
305 self._funcOrLambda(node, gen, 0)
306
307 def _funcOrLambda(self, node, gen, ndecorators):
308 """Helper for visitFunction and visitLambda."""
309 gen.Start()
310 gen.FindLocals()
311 gen.Dispatch(node.code)
312 gen.Finish()
313
314 self.set_lineno(node)
315 for default in node.defaults:
316 self.visit(default)
317 self._makeClosure(gen, len(node.defaults))
318 for i in xrange(ndecorators):
319 self.emit('CALL_FUNCTION', 1)
320
321 def visitClass(self, node):
322 frame = pyassem.Frame(node.name, self.ctx.filename, optimized=0, klass=1)
323 graph = pyassem.FlowGraph()
324 gen = ClassCodeGenerator(self.ctx, frame, graph, node)
325
326 gen.Start()
327 gen.FindLocals()
328 gen.Dispatch(node.code)
329 gen.Finish()
330
331 self.set_lineno(node)
332 self.emit('LOAD_CONST', node.name)
333 for base in node.bases:
334 self.visit(base)
335 self.emit('BUILD_TUPLE', len(node.bases))
336 self._makeClosure(gen, 0)
337 self.emit('CALL_FUNCTION', 0)
338 self.emit('BUILD_CLASS')
339 self.storeName(node.name)
340
341 # The next few implement control-flow statements
342
343 def visitIf(self, node):
344 end = self.newBlock()
345 for i, (test, suite) in enumerate(node.tests):
346 if is_constant_false(test):
347 # XXX will need to check generator stuff here
348 continue
349 self.set_lineno(test)
350 self.visit(test)
351 nextTest = self.newBlock()
352 self.emit('POP_JUMP_IF_FALSE', nextTest)
353 self.nextBlock()
354 self.visit(suite)
355 self.emit('JUMP_FORWARD', end)
356 self.startBlock(nextTest)
357 if node.else_:
358 self.visit(node.else_)
359 self.nextBlock(end)
360
361 def visitWhile(self, node):
362 self.set_lineno(node)
363
364 loop = self.newBlock()
365 else_ = self.newBlock()
366
367 after = self.newBlock()
368 self.emit('SETUP_LOOP', after)
369
370 self.nextBlock(loop)
371 self.setups.push((LOOP, loop))
372
373 self.set_lineno(node, force=True)
374 self.visit(node.test)
375 self.emit('POP_JUMP_IF_FALSE', else_ or after)
376
377 self.nextBlock()
378 self.visit(node.body)
379 self.emit('JUMP_ABSOLUTE', loop)
380
381 self.startBlock(else_) # or just the POPs if not else clause
382 self.emit('POP_BLOCK')
383 self.setups.pop()
384 if node.else_:
385 self.visit(node.else_)
386 self.nextBlock(after)
387
388 def visitFor(self, node):
389 start = self.newBlock()
390 anchor = self.newBlock()
391 after = self.newBlock()
392 self.setups.push((LOOP, start))
393
394 self.set_lineno(node)
395 self.emit('SETUP_LOOP', after)
396 self.visit(node.list)
397 self.emit('GET_ITER')
398
399 self.nextBlock(start)
400 self.set_lineno(node, force=True)
401 self.emit('FOR_ITER', anchor)
402 self.visit(node.assign)
403 self.visit(node.body)
404 self.emit('JUMP_ABSOLUTE', start)
405 self.nextBlock(anchor)
406 self.emit('POP_BLOCK')
407 self.setups.pop()
408 if node.else_:
409 self.visit(node.else_)
410 self.nextBlock(after)
411
412 def visitBreak(self, node):
413 if not self.setups:
414 raise SyntaxError, "'break' outside loop (%s, %d)" % \
415 (self.ctx.filename, node.lineno)
416 self.set_lineno(node)
417 self.emit('BREAK_LOOP')
418
419 def visitContinue(self, node):
420 if not self.setups:
421 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
422 (self.ctx.filename, node.lineno)
423 kind, block = self.setups.top()
424 if kind == LOOP:
425 self.set_lineno(node)
426 self.emit('JUMP_ABSOLUTE', block)
427 self.nextBlock()
428 elif kind == EXCEPT or kind == TRY_FINALLY:
429 self.set_lineno(node)
430 # find the block that starts the loop
431 top = len(self.setups)
432 while top > 0:
433 top = top - 1
434 kind, loop_block = self.setups[top]
435 if kind == LOOP:
436 break
437 if kind != LOOP:
438 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
439 (self.ctx.filename, node.lineno)
440 self.emit('CONTINUE_LOOP', loop_block)
441 self.nextBlock()
442 elif kind == END_FINALLY:
443 msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
444 raise SyntaxError, msg % (self.ctx.filename, node.lineno)
445
446 def visitTest(self, node, jump):
447 end = self.newBlock()
448 for child in node.nodes[:-1]:
449 self.visit(child)
450 self.emit(jump, end)
451 self.nextBlock()
452 self.visit(node.nodes[-1])
453 self.nextBlock(end)
454
455 def visitAnd(self, node):
456 self.visitTest(node, 'JUMP_IF_FALSE_OR_POP')
457
458 def visitOr(self, node):
459 self.visitTest(node, 'JUMP_IF_TRUE_OR_POP')
460
461 def visitIfExp(self, node):
462 endblock = self.newBlock()
463 elseblock = self.newBlock()
464 self.visit(node.test)
465 self.emit('POP_JUMP_IF_FALSE', elseblock)
466 self.visit(node.then)
467 self.emit('JUMP_FORWARD', endblock)
468 self.nextBlock(elseblock)
469 self.visit(node.else_)
470 self.nextBlock(endblock)
471
472 def visitCompare(self, node):
473 self.visit(node.expr)
474 cleanup = self.newBlock()
475 for op, code in node.ops[:-1]:
476 self.visit(code)
477 self.emit('DUP_TOP')
478 self.emit('ROT_THREE')
479 self.emit('COMPARE_OP', op)
480 self.emit('JUMP_IF_FALSE_OR_POP', cleanup)
481 self.nextBlock()
482 # now do the last comparison
483 if node.ops:
484 op, code = node.ops[-1]
485 self.visit(code)
486 self.emit('COMPARE_OP', op)
487 if len(node.ops) > 1:
488 end = self.newBlock()
489 self.emit('JUMP_FORWARD', end)
490 self.startBlock(cleanup)
491 self.emit('ROT_TWO')
492 self.emit('POP_TOP')
493 self.nextBlock(end)
494
495 # list comprehensions
496 def visitListComp(self, node):
497 self.set_lineno(node)
498 # setup list
499 self.emit('BUILD_LIST', 0)
500
501 stack = []
502 for i, for_ in enumerate(node.quals):
503 start, anchor = self.visit(for_)
504 cont = None
505 for if_ in for_.ifs:
506 if cont is None:
507 cont = self.newBlock()
508 self.visit(if_, cont)
509 stack.insert(0, (start, cont, anchor))
510
511 self.visit(node.expr)
512 self.emit('LIST_APPEND', len(node.quals) + 1)
513
514 for start, cont, anchor in stack:
515 if cont:
516 self.nextBlock(cont)
517 self.emit('JUMP_ABSOLUTE', start)
518 self.startBlock(anchor)
519
520 def visitSetComp(self, node):
521 self.set_lineno(node)
522 # setup list
523 self.emit('BUILD_SET', 0)
524
525 stack = []
526 for i, for_ in enumerate(node.quals):
527 start, anchor = self.visit(for_)
528 cont = None
529 for if_ in for_.ifs:
530 if cont is None:
531 cont = self.newBlock()
532 self.visit(if_, cont)
533 stack.insert(0, (start, cont, anchor))
534
535 self.visit(node.expr)
536 self.emit('SET_ADD', len(node.quals) + 1)
537
538 for start, cont, anchor in stack:
539 if cont:
540 self.nextBlock(cont)
541 self.emit('JUMP_ABSOLUTE', start)
542 self.startBlock(anchor)
543
544 def visitDictComp(self, node):
545 self.set_lineno(node)
546 # setup list
547 self.emit('BUILD_MAP', 0)
548
549 stack = []
550 for i, for_ in enumerate(node.quals):
551 start, anchor = self.visit(for_)
552 cont = None
553 for if_ in for_.ifs:
554 if cont is None:
555 cont = self.newBlock()
556 self.visit(if_, cont)
557 stack.insert(0, (start, cont, anchor))
558
559 self.visit(node.value)
560 self.visit(node.key)
561 self.emit('MAP_ADD', len(node.quals) + 1)
562
563 for start, cont, anchor in stack:
564 if cont:
565 self.nextBlock(cont)
566 self.emit('JUMP_ABSOLUTE', start)
567 self.startBlock(anchor)
568
569 def visitListCompFor(self, node):
570 start = self.newBlock()
571 anchor = self.newBlock()
572
573 self.visit(node.list)
574 self.emit('GET_ITER')
575 self.nextBlock(start)
576 self.set_lineno(node, force=True)
577 self.emit('FOR_ITER', anchor)
578 self.nextBlock()
579 self.visit(node.assign)
580 return start, anchor
581
582 def visitListCompIf(self, node, branch):
583 self.set_lineno(node, force=True)
584 self.visit(node.test)
585 self.emit('POP_JUMP_IF_FALSE', branch)
586 self.newBlock()
587
588 def _makeClosure(self, gen, args):
589 """Emit LOAD_CONST of this generator.
590
591 ArgEncoder calls MakeCodeObject on it. There are a few instructions
592 afterward, so I guess we can't do that here?
593 """
594 # NOTE: This scope analysis must be wrong, because we are outputting
595 # LOAD_CLOSURE.
596 frees = gen.scope.get_free_vars()
597
598 # Recursive call!
599 co = pyassem.MakeCodeObject(gen.frame, gen.graph, self.ctx.comp_opt)
600
601 if frees:
602 for name in frees:
603 self.emit('LOAD_CLOSURE', name)
604 self.emit('BUILD_TUPLE', len(frees))
605 self.emit('LOAD_CONST', co)
606 self.emit('MAKE_CLOSURE', args)
607 else:
608 self.emit('LOAD_CONST', co)
609 self.emit('MAKE_FUNCTION', args)
610
611 def visitGenExpr(self, node):
612 isLambda = 1 # TODO: Shouldn't be a lambda? Note Finish().
613 if isLambda:
614 obj_name = "<lambda.%d>" % gLambdaCounter.next()
615 else:
616 # TODO: enable this. This is more like CPython. Note that I worked
617 # worked around a bug in byterun due to NOT having this.
618 # http://bugs.python.org/issue19611
619 # That workaround may no longer be necessary if we switch.
620 obj_name = '<genexpr>'
621
622 frame = pyassem.Frame(obj_name, self.ctx.filename, optimized=1)
623 frame.setArgs(node.argnames)
624 graph = pyassem.FlowGraph()
625 gen = GenExprCodeGenerator(self.ctx, frame, graph, node,
626 self.class_name)
627
628 gen.Start()
629 gen.FindLocals()
630 gen.Dispatch(node.code)
631 gen.Finish()
632
633 self.set_lineno(node)
634 self._makeClosure(gen, 0)
635 # precomputation of outmost iterable
636 self.visit(node.code.quals[0].iter)
637 self.emit('GET_ITER')
638 self.emit('CALL_FUNCTION', 1)
639
640 def visitGenExprInner(self, node):
641 self.set_lineno(node)
642 # setup list
643
644 stack = []
645 for i, for_ in enumerate(node.quals):
646 start, anchor, end = self.visit(for_)
647 cont = None
648 for if_ in for_.ifs:
649 if cont is None:
650 cont = self.newBlock()
651 self.visit(if_, cont)
652 stack.insert(0, (start, cont, anchor, end))
653
654 self.visit(node.expr)
655 self.emit('YIELD_VALUE')
656 self.emit('POP_TOP')
657
658 for start, cont, anchor, end in stack:
659 if cont:
660 self.nextBlock(cont)
661 self.emit('JUMP_ABSOLUTE', start)
662 self.startBlock(anchor)
663 self.emit('POP_BLOCK')
664 self.setups.pop()
665 self.nextBlock(end)
666
667 self.emit('LOAD_CONST', None)
668
669 def visitGenExprFor(self, node):
670 start = self.newBlock()
671 anchor = self.newBlock()
672 end = self.newBlock()
673
674 self.setups.push((LOOP, start))
675 self.emit('SETUP_LOOP', end)
676
677 if node.is_outmost:
678 self.loadName('.0')
679 else:
680 self.visit(node.iter)
681 self.emit('GET_ITER')
682
683 self.nextBlock(start)
684 self.set_lineno(node, force=True)
685 self.emit('FOR_ITER', anchor)
686 self.nextBlock()
687 self.visit(node.assign)
688 return start, anchor, end
689
690 def visitGenExprIf(self, node, branch):
691 self.set_lineno(node, force=True)
692 self.visit(node.test)
693 self.emit('POP_JUMP_IF_FALSE', branch)
694 self.newBlock()
695
696 # exception related
697
698 def visitAssert(self, node):
699 # XXX would be interesting to implement this via a
700 # transformation of the AST before this stage
701 if __debug__:
702 end = self.newBlock()
703 self.set_lineno(node)
704 # XXX AssertionError appears to be special case -- it is always
705 # loaded as a global even if there is a local name. I guess this
706 # is a sort of renaming op.
707 self.nextBlock()
708 self.visit(node.test)
709 self.emit('POP_JUMP_IF_TRUE', end)
710 self.nextBlock()
711 self.emit('LOAD_GLOBAL', 'AssertionError')
712 if node.fail:
713 self.visit(node.fail)
714 self.emit('RAISE_VARARGS', 2)
715 else:
716 self.emit('RAISE_VARARGS', 1)
717 self.nextBlock(end)
718
719 def visitRaise(self, node):
720 self.set_lineno(node)
721 n = 0
722 if node.expr1:
723 self.visit(node.expr1)
724 n = n + 1
725 if node.expr2:
726 self.visit(node.expr2)
727 n = n + 1
728 if node.expr3:
729 self.visit(node.expr3)
730 n = n + 1
731 self.emit('RAISE_VARARGS', n)
732
733 def visitTryExcept(self, node):
734 body = self.newBlock()
735 handlers = self.newBlock()
736 end = self.newBlock()
737 if node.else_:
738 lElse = self.newBlock()
739 else:
740 lElse = end
741 self.set_lineno(node)
742 self.emit('SETUP_EXCEPT', handlers)
743 self.nextBlock(body)
744 self.setups.push((EXCEPT, body))
745 self.visit(node.body)
746 self.emit('POP_BLOCK')
747 self.setups.pop()
748 self.emit('JUMP_FORWARD', lElse)
749 self.startBlock(handlers)
750
751 last = len(node.handlers) - 1
752 for i, (expr, target, body) in enumerate(node.handlers):
753 expr, target, body = node.handlers[i]
754 self.set_lineno(expr)
755 if expr:
756 self.emit('DUP_TOP')
757 self.visit(expr)
758 self.emit('COMPARE_OP', 'exception match')
759 next = self.newBlock()
760 self.emit('POP_JUMP_IF_FALSE', next)
761 self.nextBlock()
762 self.emit('POP_TOP')
763 if target:
764 self.visit(target)
765 else:
766 self.emit('POP_TOP')
767 self.emit('POP_TOP')
768 self.visit(body)
769 self.emit('JUMP_FORWARD', end)
770 if expr:
771 self.nextBlock(next)
772 else:
773 self.nextBlock()
774 self.emit('END_FINALLY')
775 if node.else_:
776 self.nextBlock(lElse)
777 self.visit(node.else_)
778 self.nextBlock(end)
779
780 def visitTryFinally(self, node):
781 body = self.newBlock()
782 final = self.newBlock()
783 self.set_lineno(node)
784 self.emit('SETUP_FINALLY', final)
785 self.nextBlock(body)
786 self.setups.push((TRY_FINALLY, body))
787 self.visit(node.body)
788 self.emit('POP_BLOCK')
789 self.setups.pop()
790 self.emit('LOAD_CONST', None)
791 self.nextBlock(final)
792 self.setups.push((END_FINALLY, final))
793 self.visit(node.final)
794 self.emit('END_FINALLY')
795 self.setups.pop()
796
797 def visitWith(self, node):
798 body = self.newBlock()
799 final = self.newBlock()
800 self.__with_count += 1
801 valuevar = "_[%d]" % self.__with_count
802 self.set_lineno(node)
803 self.visit(node.expr)
804 self.emit('DUP_TOP')
805 self.emit('LOAD_ATTR', '__exit__')
806 self.emit('ROT_TWO')
807 self.emit('LOAD_ATTR', '__enter__')
808 self.emit('CALL_FUNCTION', 0)
809 if node.vars is None:
810 self.emit('POP_TOP')
811 else:
812 self._implicitNameOp('STORE', valuevar)
813 self.emit('SETUP_FINALLY', final)
814 self.nextBlock(body)
815 self.setups.push((TRY_FINALLY, body))
816
817 # TODO: This is buggy:
818 # - It generates LOAD_FAST and DELETE_FAST in bin/oil.py.
819 # The self.visit does nothing -- there are never any statements in between.
820 # - I think WITH_CLEANUP pops a block so this is # OK.
821 # - It never emits SETUP_WITH! Doh!
822
823 if node.vars is not None:
824 self._implicitNameOp('LOAD', valuevar)
825 self._implicitNameOp('DELETE', valuevar)
826 self.visit(node.vars)
827 self.visit(node.body)
828 self.emit('POP_BLOCK')
829 self.setups.pop()
830 self.emit('LOAD_CONST', None)
831 self.nextBlock(final)
832 self.setups.push((END_FINALLY, final))
833 self.emit('WITH_CLEANUP')
834 self.emit('END_FINALLY')
835 self.setups.pop()
836 self.__with_count -= 1
837
838 # misc
839
840 def visitDiscard(self, node):
841 self.set_lineno(node)
842 self.visit(node.expr)
843 self.emit('POP_TOP')
844
845 def visitConst(self, node):
846 self.emit('LOAD_CONST', node.value)
847
848 def visitKeyword(self, node):
849 self.emit('LOAD_CONST', node.name)
850 self.visit(node.expr)
851
852 def visitGlobal(self, node):
853 # no code to generate
854 pass
855
856 def visitName(self, node):
857 self.set_lineno(node)
858 self.loadName(node.name)
859
860 def visitPass(self, node):
861 self.set_lineno(node)
862
863 def visitImport(self, node):
864 self.set_lineno(node)
865 level = 0 if self.frame.checkFlag(CO_FUTURE_ABSIMPORT) else -1
866 for name, alias in node.names:
867 self.emit('LOAD_CONST', level)
868 self.emit('LOAD_CONST', None)
869 self.emit('IMPORT_NAME', name)
870 mod = name.split(".")[0]
871 if alias:
872 self._resolveDots(name)
873 self.storeName(alias)
874 else:
875 self.storeName(mod)
876
877 def visitFrom(self, node):
878 self.set_lineno(node)
879 level = node.level
880 if level == 0 and not self.frame.checkFlag(CO_FUTURE_ABSIMPORT):
881 level = -1
882 fromlist = tuple(name for (name, alias) in node.names)
883 self.emit('LOAD_CONST', level)
884 self.emit('LOAD_CONST', fromlist)
885 self.emit('IMPORT_NAME', node.modname)
886 for name, alias in node.names:
887 if name == '*':
888 self.namespace = 0
889 self.emit('IMPORT_STAR')
890 # There can only be one name w/ from ... import *
891 assert len(node.names) == 1
892 return
893 else:
894 self.emit('IMPORT_FROM', name)
895 self._resolveDots(name)
896 self.storeName(alias or name)
897 self.emit('POP_TOP')
898
899 def _resolveDots(self, name):
900 elts = name.split(".")
901 if len(elts) == 1:
902 return
903 for elt in elts[1:]:
904 self.emit('LOAD_ATTR', elt)
905
906 def visitGetattr(self, node):
907 self.visit(node.expr)
908 self.emit('LOAD_ATTR', self._mangle(node.attrname))
909
910 # next five implement assignments
911
912 def visitAssign(self, node):
913 self.set_lineno(node)
914 self.visit(node.expr)
915 dups = len(node.nodes) - 1
916 for i, elt in enumerate(node.nodes):
917 if i < dups:
918 self.emit('DUP_TOP')
919 if isinstance(elt, ast.Node):
920 self.visit(elt)
921
922 def visitAssName(self, node):
923 if node.flags == 'OP_ASSIGN':
924 self.storeName(node.name)
925 elif node.flags == 'OP_DELETE':
926 self.set_lineno(node)
927 self.delName(node.name)
928 else:
929 print("oops", node.flags)
930
931 def visitAssAttr(self, node):
932 self.visit(node.expr)
933 if node.flags == 'OP_ASSIGN':
934 self.emit('STORE_ATTR', self._mangle(node.attrname))
935 elif node.flags == 'OP_DELETE':
936 self.emit('DELETE_ATTR', self._mangle(node.attrname))
937 else:
938 print("warning: unexpected flags:", node.flags)
939 print(node)
940
941 def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
942 v = OpFinder()
943 v.Dispatch(node)
944
945 if v.op != 'OP_DELETE':
946 self.emit(op, len(node.nodes))
947 for child in node.nodes:
948 self.visit(child)
949
950 visitAssTuple = _visitAssSequence
951 visitAssList = _visitAssSequence
952
953 # augmented assignment
954
955 def visitAugAssign(self, node):
956 self.set_lineno(node)
957 aug_node = wrap_aug(node.node)
958 self.visit(aug_node, "load")
959 self.visit(node.expr)
960 self.emit(_AUGMENTED_OPCODE[node.op])
961 self.visit(aug_node, "store")
962
963 def visitAugName(self, node, mode):
964 if mode == "load":
965 self.loadName(node.name)
966 elif mode == "store":
967 self.storeName(node.name)
968
969 def visitAugGetattr(self, node, mode):
970 if mode == "load":
971 self.visit(node.expr)
972 self.emit('DUP_TOP')
973 self.emit('LOAD_ATTR', self._mangle(node.attrname))
974 elif mode == "store":
975 self.emit('ROT_TWO')
976 self.emit('STORE_ATTR', self._mangle(node.attrname))
977
978 def visitAugSlice(self, node, mode):
979 if mode == "load":
980 self.visitSlice(node, 1)
981 elif mode == "store":
982 slice = 0
983 if node.lower:
984 slice = slice | 1
985 if node.upper:
986 slice = slice | 2
987 if slice == 0:
988 self.emit('ROT_TWO')
989 elif slice == 3:
990 self.emit('ROT_FOUR')
991 else:
992 self.emit('ROT_THREE')
993 self.emit('STORE_SLICE+%d' % slice)
994
995 def visitAugSubscript(self, node, mode):
996 if mode == "load":
997 self.visitSubscript(node, 1)
998 elif mode == "store":
999 self.emit('ROT_THREE')
1000 self.emit('STORE_SUBSCR')
1001
1002 def visitExec(self, node):
1003 self.visit(node.expr)
1004 if node.locals is None:
1005 self.emit('LOAD_CONST', None)
1006 else:
1007 self.visit(node.locals)
1008 if node.globals is None:
1009 self.emit('DUP_TOP')
1010 else:
1011 self.visit(node.globals)
1012 self.emit('EXEC_STMT')
1013
1014 def visitCallFunc(self, node):
1015 pos = 0
1016 kw = 0
1017 self.set_lineno(node)
1018 self.visit(node.node)
1019 for arg in node.args:
1020 self.visit(arg)
1021 if isinstance(arg, ast.Keyword):
1022 kw = kw + 1
1023 else:
1024 pos = pos + 1
1025 if node.star_args is not None:
1026 self.visit(node.star_args)
1027 if node.dstar_args is not None:
1028 self.visit(node.dstar_args)
1029 have_star = node.star_args is not None
1030 have_dstar = node.dstar_args is not None
1031 opcode = _CALLFUNC_OPCODE_INFO[have_star, have_dstar]
1032 self.emit(opcode, kw << 8 | pos)
1033
1034 def visitPrint(self, node, newline=0):
1035 self.set_lineno(node)
1036 if node.dest:
1037 self.visit(node.dest)
1038 for child in node.nodes:
1039 if node.dest:
1040 self.emit('DUP_TOP')
1041 self.visit(child)
1042 if node.dest:
1043 self.emit('ROT_TWO')
1044 self.emit('PRINT_ITEM_TO')
1045 else:
1046 self.emit('PRINT_ITEM')
1047 if node.dest and not newline:
1048 self.emit('POP_TOP')
1049
1050 def visitPrintnl(self, node):
1051 self.visitPrint(node, newline=1)
1052 if node.dest:
1053 self.emit('PRINT_NEWLINE_TO')
1054 else:
1055 self.emit('PRINT_NEWLINE')
1056
1057 def visitReturn(self, node):
1058 self.set_lineno(node)
1059 self.visit(node.value)
1060 self.emit('RETURN_VALUE')
1061
1062 def visitYield(self, node):
1063 self.set_lineno(node)
1064 self.visit(node.value)
1065 self.emit('YIELD_VALUE')
1066
1067 # slice and subscript stuff
1068
1069 def visitSlice(self, node, aug_flag=None):
1070 # aug_flag is used by visitAugSlice
1071 self.visit(node.expr)
1072 slice = 0
1073 if node.lower:
1074 self.visit(node.lower)
1075 slice = slice | 1
1076 if node.upper:
1077 self.visit(node.upper)
1078 slice = slice | 2
1079 if aug_flag:
1080 if slice == 0:
1081 self.emit('DUP_TOP')
1082 elif slice == 3:
1083 self.emit('DUP_TOPX', 3)
1084 else:
1085 self.emit('DUP_TOPX', 2)
1086 if node.flags == 'OP_APPLY':
1087 self.emit('SLICE+%d' % slice)
1088 elif node.flags == 'OP_ASSIGN':
1089 self.emit('STORE_SLICE+%d' % slice)
1090 elif node.flags == 'OP_DELETE':
1091 self.emit('DELETE_SLICE+%d' % slice)
1092 else:
1093 print("weird slice", node.flags)
1094 raise
1095
1096 def visitSubscript(self, node, aug_flag=None):
1097 self.visit(node.expr)
1098 for sub in node.subs:
1099 self.visit(sub)
1100 if len(node.subs) > 1:
1101 self.emit('BUILD_TUPLE', len(node.subs))
1102 if aug_flag:
1103 self.emit('DUP_TOPX', 2)
1104 if node.flags == 'OP_APPLY':
1105 self.emit('BINARY_SUBSCR')
1106 elif node.flags == 'OP_ASSIGN':
1107 self.emit('STORE_SUBSCR')
1108 elif node.flags == 'OP_DELETE':
1109 self.emit('DELETE_SUBSCR')
1110
1111 # binary ops
1112
1113 def binaryOp(self, node, op):
1114 self.visit(node.left)
1115 self.visit(node.right)
1116 self.emit(op)
1117
1118 def visitAdd(self, node):
1119 return self.binaryOp(node, 'BINARY_ADD')
1120
1121 def visitSub(self, node):
1122 return self.binaryOp(node, 'BINARY_SUBTRACT')
1123
1124 def visitMul(self, node):
1125 return self.binaryOp(node, 'BINARY_MULTIPLY')
1126
1127 def visitDiv(self, node):
1128 return self.binaryOp(node, self._div_op)
1129
1130 def visitFloorDiv(self, node):
1131 return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE')
1132
1133 def visitMod(self, node):
1134 return self.binaryOp(node, 'BINARY_MODULO')
1135
1136 def visitPower(self, node):
1137 return self.binaryOp(node, 'BINARY_POWER')
1138
1139 def visitLeftShift(self, node):
1140 return self.binaryOp(node, 'BINARY_LSHIFT')
1141
1142 def visitRightShift(self, node):
1143 return self.binaryOp(node, 'BINARY_RSHIFT')
1144
1145 # unary ops
1146
1147 def unaryOp(self, node, op):
1148 self.visit(node.expr)
1149 self.emit(op)
1150
1151 def visitInvert(self, node):
1152 return self.unaryOp(node, 'UNARY_INVERT')
1153
1154 def visitUnarySub(self, node):
1155 return self.unaryOp(node, 'UNARY_NEGATIVE')
1156
1157 def visitUnaryAdd(self, node):
1158 return self.unaryOp(node, 'UNARY_POSITIVE')
1159
1160 def visitUnaryInvert(self, node):
1161 return self.unaryOp(node, 'UNARY_INVERT')
1162
1163 def visitNot(self, node):
1164 return self.unaryOp(node, 'UNARY_NOT')
1165
1166 def visitBackquote(self, node):
1167 return self.unaryOp(node, 'UNARY_CONVERT')
1168
1169 # bit ops
1170
1171 def bitOp(self, nodes, op):
1172 self.visit(nodes[0])
1173 for node in nodes[1:]:
1174 self.visit(node)
1175 self.emit(op)
1176
1177 def visitBitand(self, node):
1178 return self.bitOp(node.nodes, 'BINARY_AND')
1179
1180 def visitBitor(self, node):
1181 return self.bitOp(node.nodes, 'BINARY_OR')
1182
1183 def visitBitxor(self, node):
1184 return self.bitOp(node.nodes, 'BINARY_XOR')
1185
1186 # object constructors
1187
1188 def visitEllipsis(self, node):
1189 self.emit('LOAD_CONST', Ellipsis)
1190
1191 def visitTuple(self, node):
1192 self.set_lineno(node)
1193 for elt in node.nodes:
1194 self.visit(elt)
1195 self.emit('BUILD_TUPLE', len(node.nodes))
1196
1197 def visitList(self, node):
1198 self.set_lineno(node)
1199 for elt in node.nodes:
1200 self.visit(elt)
1201 self.emit('BUILD_LIST', len(node.nodes))
1202
1203 def visitSet(self, node):
1204 self.set_lineno(node)
1205 for elt in node.nodes:
1206 self.visit(elt)
1207 self.emit('BUILD_SET', len(node.nodes))
1208
1209 def visitSliceobj(self, node):
1210 for child in node.nodes:
1211 self.visit(child)
1212 self.emit('BUILD_SLICE', len(node.nodes))
1213
1214 def visitDict(self, node):
1215 self.set_lineno(node)
1216 self.emit('BUILD_MAP', 0)
1217 for k, v in node.items:
1218 self.emit('DUP_TOP')
1219 self.visit(k)
1220 self.visit(v)
1221 self.emit('ROT_THREE')
1222 self.emit('STORE_SUBSCR')
1223
1224
1225class TopLevelCodeGenerator(CodeGenerator):
1226
1227 def Finish(self):
1228 pass
1229
1230
1231class InteractiveCodeGenerator(TopLevelCodeGenerator):
1232
1233 def visitDiscard(self, node):
1234 # XXX Discard means it's an expression. Perhaps this is a bad
1235 # name.
1236 self.visit(node.expr)
1237 self.emit('PRINT_EXPR')
1238
1239 def Finish(self):
1240 # Not sure why I need this?
1241 self.emit('RETURN_VALUE')
1242
1243
1244# NOTE: This feature removed in Python 3! I didn't even know about it!
1245#
1246# https://www.python.org/dev/peps/pep-3113/
1247# def fxn(a, (b, c), d):
1248# pass
1249
1250def _CheckNoTupleArgs(func):
1251 for i, arg in enumerate(func.argnames):
1252 if isinstance(arg, tuple):
1253 raise RuntimeError(
1254 'Tuple args are not supported: %s in function %s' %
1255 (arg, func.name))
1256
1257
1258class _FunctionCodeGenerator(CodeGenerator):
1259 """Abstract class."""
1260
1261 def __init__(self, ctx, frame, graph, func, class_name):
1262 CodeGenerator.__init__(self, ctx, frame, graph)
1263 self.func = func
1264 self.class_name = class_name
1265
1266 self.scope = self.ctx.scopes[func]
1267
1268 def _optimized(self):
1269 # -fast-ops=1 is the default
1270 return self.ctx.comp_opt.fast_ops
1271
1272 def FindLocals(self):
1273 func = self.func
1274
1275 lnf = LocalNameFinder(set(self.func.argnames))
1276 lnf.Dispatch(func.code)
1277 self.locals.push(lnf.getLocals())
1278
1279 if func.varargs:
1280 self.frame.setFlag(CO_VARARGS)
1281 if func.kwargs:
1282 self.frame.setFlag(CO_VARKEYWORDS)
1283 self.set_lineno(func)
1284
1285
1286class FunctionCodeGenerator(_FunctionCodeGenerator):
1287
1288 def _Start(self):
1289 if self.scope.generator is not None: # does it have yield in it?
1290 self.frame.setFlag(CO_GENERATOR)
1291 if self.func.doc:
1292 self.frame.setDocstring(self.func.doc)
1293
1294 def Finish(self):
1295 self.graph.startExitBlock()
1296 self.emit('LOAD_CONST', None)
1297 self.emit('RETURN_VALUE')
1298
1299
1300class LambdaCodeGenerator(_FunctionCodeGenerator):
1301
1302 def _Start(self):
1303 if self.scope.generator is not None: # does it have yield in it?
1304 self.frame.setFlag(CO_GENERATOR)
1305
1306 def Finish(self):
1307 self.graph.startExitBlock()
1308 self.emit('RETURN_VALUE')
1309
1310
1311class GenExprCodeGenerator(_FunctionCodeGenerator):
1312
1313 def _Start(self):
1314 self.frame.setFlag(CO_GENERATOR) # It's always a generator
1315
1316 def Finish(self):
1317 self.graph.startExitBlock()
1318 self.emit('RETURN_VALUE')
1319
1320
1321class ClassCodeGenerator(CodeGenerator):
1322
1323 def __init__(self, ctx, frame, graph, klass):
1324 CodeGenerator.__init__(self, ctx, frame, graph)
1325 self.klass = klass
1326
1327 self.class_name = klass.name
1328 self.scope = self.ctx.scopes[klass]
1329
1330 def _Start(self):
1331 self.set_lineno(self.klass)
1332 self.emit("LOAD_GLOBAL", "__name__")
1333 self.storeName("__module__")
1334 if self.klass.doc and self.ctx.comp_opt.emit_docstring:
1335 self.emit("LOAD_CONST", self.klass.doc)
1336 self.storeName('__doc__')
1337
1338 def FindLocals(self):
1339 lnf = LocalNameFinder()
1340 lnf.Dispatch(self.klass.code)
1341 self.locals.push(lnf.getLocals())
1342
1343 self.frame.setFlag(CO_NEWLOCALS)
1344 if self.klass.doc:
1345 self.frame.setDocstring(self.klass.doc)
1346
1347 def Finish(self):
1348 self.graph.startExitBlock()
1349 self.emit('LOAD_LOCALS')
1350 self.emit('RETURN_VALUE')
1351
1352
1353class OpFinder(ASTVisitor):
1354 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1355
1356 def __init__(self):
1357 ASTVisitor.__init__(self)
1358 self.op = None
1359
1360 def visitAssName(self, node):
1361 if self.op is None:
1362 self.op = node.flags
1363 elif self.op != node.flags:
1364 raise ValueError, "mixed ops in stmt"
1365
1366 visitAssAttr = visitAssName
1367 visitSubscript = visitAssName
1368
1369
1370# TODO: I don't understand this. It looks like it does nothing, but removing
1371# wrap_aug() breaks.
1372class Delegator(object):
1373 """Base class to support delegation for augmented assignment nodes
1374
1375 To generator code for augmented assignments, we use the following
1376 wrapper classes. In visitAugAssign, the left-hand expression node
1377 is visited twice. The first time the visit uses the normal method
1378 for that node . The second time the visit uses a different method
1379 that generates the appropriate code to perform the assignment.
1380 These delegator classes wrap the original AST nodes in order to
1381 support the variant visit methods.
1382 """
1383 def __init__(self, obj):
1384 self.obj = obj
1385
1386 def __getattr__(self, attr):
1387 return getattr(self.obj, attr)
1388
1389
1390class AugGetattr(Delegator):
1391 pass
1392
1393class AugName(Delegator):
1394 pass
1395
1396class AugSlice(Delegator):
1397 pass
1398
1399class AugSubscript(Delegator):
1400 pass
1401
1402AUG_WRAPPER = {
1403 ast.Getattr: AugGetattr,
1404 ast.Name: AugName,
1405 ast.Slice: AugSlice,
1406 ast.Subscript: AugSubscript,
1407 }
1408
1409def wrap_aug(node):
1410 return AUG_WRAPPER[node.__class__](node)