this repo has no description
1# Portions copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2# pyre-unsafe
3from __future__ import annotations
4
5import ast
6import importlib.util
7import itertools
8import marshal
9import os
10import sys
11from ast import AST, ClassDef
12from builtins import compile as builtin_compile
13from contextlib import contextmanager
14
15from . import consts36, consts38, future, misc, pyassem, symbols
16from .consts import (
17 CO_ASYNC_GENERATOR,
18 CO_COROUTINE,
19 CO_GENERATOR,
20 CO_NESTED,
21 CO_VARARGS,
22 CO_VARKEYWORDS,
23 SC_CELL,
24 SC_FREE,
25 SC_GLOBAL_EXPLICIT,
26 SC_GLOBAL_IMPLICIT,
27 SC_LOCAL,
28 PyCF_MASK_OBSOLETE,
29 PyCF_ONLY_AST,
30 PyCF_SOURCE_IS_UTF8,
31)
32from .optimizer import AstOptimizer
33from .py38.optimizer import AstOptimizer38
34from .pyassem import PyFlowGraph
35from .symbols import SymbolVisitor
36from .unparse import to_expr
37from .visitor import ASTVisitor, walk
38
39TYPE_CHECKING = False
40if TYPE_CHECKING:
41 from typing import List, Optional, Sequence, Union, Type
42
43try:
44 import _parser # pyre-ignore[21]
45except ImportError:
46 parse_callable = builtin_compile
47else:
48 parse_callable = _parser.parse
49
50
51callfunc_opcode_info = {
52 # (Have *args, Have **args) : opcode
53 (0, 0): "CALL_FUNCTION",
54 (1, 0): "CALL_FUNCTION_VAR",
55 (0, 1): "CALL_FUNCTION_KW",
56 (1, 1): "CALL_FUNCTION_VAR_KW",
57}
58
59# This is a super set of all Python versions
60LOOP = 1
61EXCEPT = 2
62TRY_FINALLY = 3
63END_FINALLY = 4
64WHILE_LOOP = 5
65FOR_LOOP = 6
66TRY_FINALLY_BREAK = 7
67WITH = 8
68ASYNC_WITH = 9
69HANDLER_CLEANUP = 10
70
71_ZERO = (0).to_bytes(4, "little")
72
73_DEFAULT_MODNAME = sys.intern("<module>")
74
75
76def make_header(mtime, size):
77 return _ZERO + mtime.to_bytes(4, "little") + size.to_bytes(4, "little")
78
79
80def compileFile(filename, display=0, compiler=None, modname=_DEFAULT_MODNAME):
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 fileinfo = os.stat(filename)
86
87 with open(filename, "U") as f:
88 buf = f.read()
89 code = compile(buf, filename, "exec", compiler=compiler, modname=modname)
90 with open(filename + "c", "wb") as f:
91 hdr = make_header(int(fileinfo.st_mtime), fileinfo.st_size)
92 f.write(importlib.util.MAGIC_NUMBER)
93 f.write(hdr)
94 marshal.dump(code, f)
95
96
97def compile(
98 source,
99 filename,
100 mode,
101 flags=0,
102 dont_inherit=None,
103 optimize=-1,
104 compiler=None,
105 modname=_DEFAULT_MODNAME,
106):
107 """Replacement for builtin compile() function"""
108 if dont_inherit is not None:
109 raise RuntimeError("not implemented yet")
110
111 result = make_compiler(source, filename, mode, flags, optimize, compiler, modname)
112 if flags & PyCF_ONLY_AST:
113 return result
114 return result.getCode()
115
116
117def parse(source, filename, mode, flags):
118 return parse_callable(source, filename, mode, flags | PyCF_ONLY_AST)
119
120
121def make_compiler(
122 source,
123 filename,
124 mode,
125 flags=0,
126 optimize=-1,
127 generator=None,
128 modname=_DEFAULT_MODNAME,
129 peephole_enabled=True,
130 ast_optimizer_enabled=True,
131):
132 if mode not in ("single", "exec", "eval"):
133 raise ValueError("compile() mode must be 'exec', 'eval' or 'single'")
134
135 if generator is None:
136 generator = get_default_generator()
137
138 consts = generator.consts
139 if flags & ~(consts.PyCF_MASK | PyCF_MASK_OBSOLETE | consts.PyCF_COMPILE_MASK):
140 raise ValueError("compile(): unrecognised flags", hex(flags))
141
142 flags |= PyCF_SOURCE_IS_UTF8
143
144 if isinstance(source, ast.AST):
145 tree = source
146 else:
147 tree = parse(source, filename, mode, flags & consts.PyCF_MASK)
148
149 if flags & PyCF_ONLY_AST:
150 return tree
151
152 optimize = sys.flags.optimize if optimize == -1 else optimize
153
154 return generator.make_code_gen(
155 modname,
156 tree,
157 filename,
158 flags,
159 optimize,
160 peephole_enabled=peephole_enabled,
161 ast_optimizer_enabled=ast_optimizer_enabled,
162 )
163
164
165def is_const(node):
166 is_const_node = isinstance(
167 node,
168 (ast.Num, ast.Str, ast.Ellipsis, ast.Bytes, ast.NameConstant, ast.Constant),
169 )
170 is_debug = isinstance(node, ast.Name) and node.id == "__debug__"
171 return is_const_node or is_debug
172
173
174def all_items_const(seq, begin, end):
175 for item in seq[begin:end]:
176 if not is_const(item):
177 return False
178 return True
179
180
181CONV_STR = ord("s")
182CONV_REPR = ord("r")
183CONV_ASCII = ord("a")
184
185
186class CodeGenerator(ASTVisitor):
187 """Defines basic code generator for Python bytecode
188
189 This class is an abstract base class. Concrete subclasses must
190 define an __init__() that defines self.graph and then calls the
191 __init__() defined in this class.
192 """
193
194 optimized = 0 # is namespace access optimized?
195 __initialized = None
196 class_name = None # provide default for instance variable
197 future_flags = 0
198 flow_graph = pyassem.PyFlowGraph
199 consts = consts36
200
201 def __init__(
202 self,
203 parent: Optional[CodeGenerator],
204 node: AST,
205 symbols: SymbolVisitor,
206 graph: PyFlowGraph,
207 flags=0,
208 optimization_lvl=0,
209 ):
210 super().__init__()
211 self.module_gen = self if parent is None else parent.module_gen
212 self.tree = node
213 self.symbols = symbols
214 self.graph = graph
215 self.scopes = symbols.scopes
216 self.setups = misc.Stack()
217 self.last_lineno = None
218 self._setupGraphDelegation()
219 self.interactive = False
220 self.graph.setFlag(self.module_gen.future_flags)
221 self.scope = self.scopes[node]
222 self.flags = flags
223 self.optimization_lvl = optimization_lvl
224 self.strip_docstrings = optimization_lvl == 2
225 self.__with_count = 0
226 self.did_setup_annotations = False
227 self._qual_name = None
228
229 def _setupGraphDelegation(self):
230 self.emit = self.graph.emit
231 self.newBlock = self.graph.newBlock
232 self.nextBlock = self.graph.nextBlock
233
234 def getCode(self):
235 """Return a code object"""
236 return self.graph.getCode()
237
238 def set_qual_name(self, qualname):
239 pass
240
241 @contextmanager
242 def noEmit(self):
243 self.graph.do_not_emit_bytecode += 1
244 try:
245 yield
246 finally:
247 self.graph.do_not_emit_bytecode -= 1
248
249 @contextmanager
250 def noOp(self):
251 yield
252
253 def maybeEmit(self, test):
254 return self.noOp() if test else self.noEmit()
255
256 def skip_visit(self):
257 """On <3.8 if we aren't emitting bytecode we shouldn't even visit the nodes."""
258 return self.graph.do_not_emit_bytecode
259
260 def mangle(self, name):
261 if self.class_name is not None:
262 return misc.mangle(name, self.class_name)
263 else:
264 return name
265
266 def get_module(self):
267 raise RuntimeError("should be implemented by subclasses")
268
269 # Next five methods handle name access
270
271 def storeName(self, name):
272 self._nameOp("STORE", name)
273
274 def loadName(self, name):
275 self._nameOp("LOAD", name)
276
277 def delName(self, name):
278 self._nameOp("DELETE", name)
279
280 def _nameOp(self, prefix, name):
281 name = self.mangle(name)
282 scope = self.scope.check_name(name)
283 if scope == SC_LOCAL:
284 if not self.optimized:
285 self.emit(prefix + "_NAME", name)
286 else:
287 self.emit(prefix + "_FAST", name)
288 elif scope == SC_GLOBAL_EXPLICIT:
289 self.emit(prefix + "_GLOBAL", name)
290 elif scope == SC_GLOBAL_IMPLICIT:
291 if not self.optimized:
292 self.emit(prefix + "_NAME", name)
293 else:
294 self.emit(prefix + "_GLOBAL", name)
295 elif scope == SC_FREE or scope == SC_CELL:
296 if isinstance(self.scope, symbols.ClassScope):
297 if prefix == "STORE" and name not in self.scope.nonlocals:
298 self.emit(prefix + "_NAME", name)
299 return
300
301 if isinstance(self.scope, symbols.ClassScope) and prefix == "LOAD":
302 self.emit(prefix + "_CLASSDEREF", name)
303 else:
304 self.emit(prefix + "_DEREF", name)
305 else:
306 raise RuntimeError("unsupported scope for var %s: %d" % (name, scope))
307
308 def _implicitNameOp(self, prefix, name):
309 """Emit name ops for names generated implicitly by for loops
310
311 The interpreter generates names that start with a period or
312 dollar sign. The symbol table ignores these names because
313 they aren't present in the program text.
314 """
315 if self.optimized:
316 self.emit(prefix + "_FAST", name)
317 else:
318 self.emit(prefix + "_NAME", name)
319
320 def set_lineno(self, node):
321 if hasattr(node, "lineno"):
322 self.graph.lineno = node.lineno
323 self.graph.lineno_set = False
324
325 def update_lineno(self, node):
326 if hasattr(node, "lineno") and node.lineno != self.graph.lineno:
327 self.set_lineno(node)
328
329 def skip_docstring(self, body):
330 """Given list of statements, representing body of a function, class,
331 or module, skip docstring, if any.
332 """
333 if (
334 body
335 and isinstance(body[0], ast.Expr)
336 and isinstance(body[0].value, ast.Str)
337 ):
338 return body[1:]
339 return body
340
341 # The first few visitor methods handle nodes that generator new
342 # code objects. They use class attributes to determine what
343 # specialized code generators to use.
344
345 def visitInteractive(self, node):
346 self.interactive = True
347 self.visit(node.body)
348 self.emit("LOAD_CONST", None)
349 self.emit("RETURN_VALUE")
350
351 def findFutures(self, node):
352 consts = self.consts
353 future_flags = self.flags & consts.PyCF_MASK
354 for feature in future.find_futures(node):
355 if feature == "generator_stop":
356 future_flags |= consts.CO_FUTURE_GENERATOR_STOP
357 elif feature == "barry_as_FLUFL":
358 future_flags |= consts.CO_FUTURE_BARRY_AS_BDFL
359 return future_flags
360
361 def visitModule(self, node):
362 self.future_flags = self.findFutures(node)
363 self.graph.setFlag(self.future_flags)
364
365 if node.body:
366 self.set_lineno(node.body[0])
367
368 # Set current line number to the line number of first statement.
369 # This way line number for SETUP_ANNOTATIONS will always
370 # coincide with the line number of first "real" statement in module.
371 # If body is empy, then lineno will be set later in assemble.
372 if self.findAnn(node.body):
373 self.emit("SETUP_ANNOTATIONS")
374 self.did_setup_annotations = True
375 doc = self.get_docstring(node)
376 if doc is not None:
377 self.emit("LOAD_CONST", doc)
378 self.storeName("__doc__")
379 self.visit(self.skip_docstring(node.body))
380
381 # See if the was a live statement, to later set its line number as
382 # module first line. If not, fall back to first line of 1.
383 if not self.graph.first_inst_lineno:
384 self.graph.first_inst_lineno = 1
385
386 self.emit_module_return(node)
387
388 def emit_module_return(self, node: ast.Module) -> None:
389 self.emit("LOAD_CONST", None)
390 self.emit("RETURN_VALUE")
391
392 def visitExpression(self, node):
393 self.visit(node.body)
394 self.emit("RETURN_VALUE")
395
396 def visitFunctionDef(self, node):
397 self.set_lineno(node)
398 self._visitFuncOrLambda(node, isLambda=0)
399 self.storeName(node.name)
400
401 visitAsyncFunctionDef = visitFunctionDef
402
403 def visitJoinedStr(self, node):
404 self.update_lineno(node)
405 for value in node.values:
406 self.visit(value)
407 if len(node.values) != 1:
408 self.emit("BUILD_STRING", len(node.values))
409
410 def visitFormattedValue(self, node):
411 self.update_lineno(node)
412 self.visit(node.value)
413
414 if node.conversion == CONV_STR:
415 oparg = pyassem.FVC_STR
416 elif node.conversion == CONV_REPR:
417 oparg = pyassem.FVC_REPR
418 elif node.conversion == CONV_ASCII:
419 oparg = pyassem.FVC_ASCII
420 else:
421 assert node.conversion == -1, str(node.conversion)
422 oparg = pyassem.FVC_NONE
423
424 if node.format_spec:
425 self.visit(node.format_spec)
426 oparg |= pyassem.FVS_HAVE_SPEC
427 self.emit("FORMAT_VALUE", oparg)
428
429 def visitLambda(self, node):
430 self.update_lineno(node)
431 self._visitFuncOrLambda(node, isLambda=1)
432
433 def processBody(self, node, body, gen):
434 if isinstance(body, list):
435 for stmt in body:
436 gen.visit(stmt)
437 else:
438 gen.visit(body)
439
440 def _visitAnnotation(self, node):
441 return self.visit(node)
442
443 skip_func_docstring = skip_docstring
444
445 def _visitFuncOrLambda(self, node, isLambda=0):
446 if not isLambda and node.decorator_list:
447 for decorator in node.decorator_list:
448 self.visit(decorator)
449 ndecorators = len(node.decorator_list)
450 first_lineno = node.decorator_list[0].lineno
451 else:
452 ndecorators = 0
453 first_lineno = node.lineno
454 flags = 0
455 name = sys.intern("<lambda>") if isLambda else node.name
456
457 gen = self.make_func_codegen(node, name, first_lineno)
458 body = node.body
459 if not isLambda:
460 body = self.skip_func_docstring(body)
461
462 self.processBody(node, body, gen)
463
464 gen.finishFunction()
465 if node.args.defaults:
466 for default in node.args.defaults:
467 self.visit(default)
468 flags |= 0x01
469 self.emit("BUILD_TUPLE", len(node.args.defaults))
470
471 kwdefaults = []
472 for kwonly, default in zip(node.args.kwonlyargs, node.args.kw_defaults):
473 if default is not None:
474 kwdefaults.append(self.mangle(kwonly.arg))
475 self.visit(default)
476
477 if kwdefaults:
478 self.emit("LOAD_CONST", tuple(kwdefaults))
479 self.emit("BUILD_CONST_KEY_MAP", len(kwdefaults))
480 flags |= 0x02
481
482 ann_args = self.annotate_args(node.args)
483 # Cannot annotate return type for lambda
484 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.returns:
485 self._visitAnnotation(node.returns)
486 ann_args.append("return")
487 if ann_args:
488 flags |= 0x04
489 self.emit("LOAD_CONST", tuple(ann_args))
490 self.emit("BUILD_CONST_KEY_MAP", len(ann_args))
491
492 self._makeClosure(gen, flags)
493
494 for _ in range(ndecorators):
495 self.emit("CALL_FUNCTION", 1)
496
497 def annotate_args(self, args: ast.arguments) -> List[str]:
498 ann_args = []
499 for arg in args.args:
500 self.annotate_arg(arg, ann_args)
501 if args.vararg:
502 # pyre-fixme[6]: Expected `arg` for 1st param but got `Optional[_ast.arg]`.
503 self.annotate_arg(args.vararg, ann_args)
504 for arg in args.kwonlyargs:
505 self.annotate_arg(arg, ann_args)
506 if args.kwarg:
507 # pyre-fixme[6]: Expected `arg` for 1st param but got `Optional[_ast.arg]`.
508 self.annotate_arg(args.kwarg, ann_args)
509 return ann_args
510
511 def annotate_arg(self, arg: ast.arg, ann_args: List[str]):
512 if arg.annotation:
513 self._visitAnnotation(arg.annotation)
514 ann_args.append(self.mangle(arg.arg))
515
516 def visitClassDef(self, node):
517 self.set_lineno(node)
518 first_lineno = None
519 for decorator in node.decorator_list:
520 if first_lineno is None:
521 first_lineno = decorator.lineno
522 self.visit(decorator)
523
524 gen = self.make_class_codegen(node, first_lineno or node.lineno)
525 gen.emit("LOAD_NAME", "__name__")
526 gen.storeName("__module__")
527 gen.emit("LOAD_CONST", gen.get_qual_prefix(gen) + gen.name)
528 gen.storeName("__qualname__")
529 if gen.findAnn(node.body):
530 gen.did_setup_annotations = True
531 gen.emit("SETUP_ANNOTATIONS")
532
533 doc = gen.get_docstring(node)
534 if doc is not None:
535 gen.update_lineno(node.body[0])
536 gen.emit("LOAD_CONST", doc)
537 gen.storeName("__doc__")
538
539 self.walkClassBody(node, gen)
540
541 gen.graph.startExitBlock()
542 if "__class__" in gen.scope.cells:
543 gen.emit("LOAD_CLOSURE", "__class__")
544 gen.emit("DUP_TOP")
545 gen.emit("STORE_NAME", "__classcell__")
546 else:
547 gen.emit("LOAD_CONST", None)
548 gen.emit("RETURN_VALUE")
549
550 self.emit("LOAD_BUILD_CLASS")
551 self._makeClosure(gen, 0)
552 self.emit("LOAD_CONST", node.name)
553
554 self._call_helper(2, node.bases, node.keywords)
555
556 for _ in range(len(node.decorator_list)):
557 self.emit("CALL_FUNCTION", 1)
558
559 self.store_type_name_and_flags(node)
560
561 def store_type_name_and_flags(self, node: ClassDef) -> None:
562 self.storeName(node.name)
563
564 def walkClassBody(self, node: ClassDef, gen: "CodeGenerator"):
565 walk(self.skip_docstring(node.body), gen)
566
567 # The rest are standard visitor methods
568
569 # The next few implement control-flow statements
570
571 def visitIf(self, node):
572 self.set_lineno(node)
573 test = node.test
574 test_const = self.get_bool_const(test)
575
576 # Emulate co_firstlineno behavior of C compiler
577 if test_const is False and not node.orelse:
578 self.graph.maybeEmitSetLineno()
579
580 end = self.newBlock("if_end")
581 orelse = None
582 if node.orelse:
583 orelse = self.newBlock("if_else")
584
585 if test_const is None:
586 self.compileJumpIf(test, orelse or end, False)
587
588 with self.maybeEmit(test_const is not False):
589 self.nextBlock()
590 self.visit(node.body)
591
592 if node.orelse:
593 if test_const is None:
594 self.emit("JUMP_FORWARD", end)
595 with self.maybeEmit(test_const is not True):
596 self.nextBlock(orelse)
597 self.visit(node.orelse)
598
599 self.nextBlock(end)
600
601 def visitWhile(self, node):
602 self.set_lineno(node)
603
604 test_const = self.get_bool_const(node.test)
605 if test_const is False:
606 if node.orelse:
607 self.visit(node.orelse)
608 return
609
610 loop = self.newBlock("while_loop")
611 else_ = self.newBlock("while_else")
612
613 after = self.newBlock("while_after")
614 self.emit("SETUP_LOOP", after)
615
616 self.nextBlock(loop)
617 self.setups.push((LOOP, loop))
618
619 if test_const is not True:
620 self.compileJumpIf(node.test, else_ or after, False)
621
622 self.nextBlock(label="while_body")
623 self.visit(node.body)
624 self.emit("JUMP_ABSOLUTE", loop)
625
626 if not self.get_bool_const(node.test):
627 self.nextBlock(else_ or after) # or just the POPs if not else clause
628
629 self.emit("POP_BLOCK")
630 self.setups.pop()
631 if node.orelse:
632 self.visit(node.orelse)
633 self.nextBlock(after)
634
635 def push_loop(self, kind, start, end):
636 self.emit("SETUP_LOOP", end)
637 self.setups.push((LOOP, start))
638
639 def pop_loop(self):
640 self.emit("POP_BLOCK")
641 self.setups.pop()
642
643 def visitFor(self, node):
644 start = self.newBlock()
645 anchor = self.newBlock()
646 after = self.newBlock()
647
648 self.set_lineno(node)
649 self.push_loop(FOR_LOOP, start, after)
650 self.visit(node.iter)
651 self.emit("GET_ITER")
652
653 self.nextBlock(start)
654 self.emit("FOR_ITER", anchor)
655 self.nextBlock()
656 self.visit(node.target)
657 self.visit(node.body)
658 self.emit("JUMP_ABSOLUTE", start)
659 self.nextBlock(anchor)
660 self.pop_loop()
661
662 if node.orelse:
663 self.visit(node.orelse)
664 self.nextBlock(after)
665
666 def emitAsyncIterYieldFrom(self):
667 self.emit("LOAD_CONST", None)
668 self.emit("YIELD_FROM")
669
670 def visitAsyncFor(self, node):
671 try_ = self.newBlock("async_for_try")
672 except_ = self.newBlock("except")
673 end = self.newBlock("end")
674 after_try = self.newBlock("after_try")
675 try_cleanup = self.newBlock("try_cleanup")
676 after_loop_else = self.newBlock("after_loop_else")
677
678 self.set_lineno(node)
679
680 self.emit("SETUP_LOOP", end)
681 self.setups.push((LOOP, try_))
682
683 self.visit(node.iter)
684 self.emit("GET_AITER")
685 self.emitAsyncIterYieldFrom()
686
687 self.nextBlock(try_)
688
689 self.emit("SETUP_EXCEPT", except_)
690 self.setups.push((EXCEPT, try_))
691
692 self.emit("GET_ANEXT")
693 self.emit("LOAD_CONST", None)
694 self.emit("YIELD_FROM")
695 self.visit(node.target)
696 self.emit("POP_BLOCK")
697 self.setups.pop()
698 self.emit("JUMP_FORWARD", after_try)
699
700 self.nextBlock(except_)
701 self.emit("DUP_TOP")
702 self.emit("LOAD_GLOBAL", "StopAsyncIteration")
703
704 self.emit("COMPARE_OP", "exception match")
705 self.emit("POP_JUMP_IF_TRUE", try_cleanup)
706 self.emit("END_FINALLY")
707
708 self.nextBlock(after_try)
709 self.visit(node.body)
710 self.emit("JUMP_ABSOLUTE", try_)
711
712 self.nextBlock(try_cleanup)
713 self.emit("POP_TOP")
714 self.emit("POP_TOP")
715 self.emit("POP_TOP")
716 self.emit("POP_EXCEPT")
717 self.emit("POP_TOP")
718 self.emit("POP_BLOCK")
719 self.setups.pop()
720
721 self.nextBlock(after_loop_else)
722
723 if node.orelse:
724 self.visit(node.orelse)
725 self.nextBlock(end)
726
727 def visitBreak(self, node):
728 if not self.setups:
729 raise SyntaxError("'break' outside loop", self.syntax_error_position(node))
730 self.set_lineno(node)
731 self.emit("BREAK_LOOP")
732
733 def visitContinue(self, node):
734 if not self.setups:
735 raise SyntaxError(
736 "'continue' not properly in loop", self.syntax_error_position(node)
737 )
738 self.set_lineno(node)
739 kind, block = self.setups.top()
740 if kind == LOOP:
741 self.emit("JUMP_ABSOLUTE", block)
742 self.nextBlock()
743 elif kind == EXCEPT or kind == TRY_FINALLY:
744 # find the block that starts the loop
745 top = len(self.setups)
746 while top > 0:
747 top = top - 1
748 kind, loop_block = self.setups[top]
749 if kind == LOOP:
750 break
751 elif kind == END_FINALLY:
752 raise SyntaxError(
753 "'continue' not supported inside 'finally' clause",
754 self.syntax_error_position(node),
755 )
756 if kind != LOOP:
757 raise SyntaxError(
758 "'continue' not properly in loop", self.syntax_error_position(node)
759 )
760 self.emit("CONTINUE_LOOP", loop_block)
761 self.nextBlock()
762 elif kind == END_FINALLY:
763 raise SyntaxError(
764 "'continue' not supported inside 'finally' clause",
765 self.syntax_error_position(node),
766 )
767
768 def syntax_error_position(self, node):
769 import linecache
770
771 source_line = linecache.getline(self.graph.filename, node.lineno)
772 return self.graph.filename, node.lineno, node.col_offset, source_line or None
773
774 def syntax_error(self, msg, node):
775 import linecache
776
777 source_line = linecache.getline(self.graph.filename, node.lineno)
778 return SyntaxError(
779 msg,
780 (self.graph.filename, node.lineno, node.col_offset, source_line or None),
781 )
782
783 def compileJumpIfPop(self, test, label, is_if_true):
784 self.visit(test)
785 self.emit(
786 "JUMP_IF_TRUE_OR_POP" if is_if_true else "JUMP_IF_FALSE_OR_POP", label
787 )
788
789 def visitTest(self, node, is_if_true: bool):
790 end = self.newBlock()
791 for child in node.values[:-1]:
792 self.compileJumpIfPop(child, end, is_if_true)
793 self.nextBlock()
794 self.visit(node.values[-1])
795 self.nextBlock(end)
796
797 def visitBoolOp(self, node):
798 self.visitTest(node, type(node.op) == ast.Or)
799
800 _cmp_opcode = {
801 ast.Eq: "==",
802 ast.NotEq: "!=",
803 ast.Lt: "<",
804 ast.LtE: "<=",
805 ast.Gt: ">",
806 ast.GtE: ">=",
807 ast.Is: "is",
808 ast.IsNot: "is not",
809 ast.In: "in",
810 ast.NotIn: "not in",
811 }
812
813 def compileJumpIf(self, test, next, is_if_true):
814 self.visit(test)
815 self.emit("POP_JUMP_IF_TRUE" if is_if_true else "POP_JUMP_IF_FALSE", next)
816
817 def visitIfExp(self, node):
818 endblock = self.newBlock()
819 elseblock = self.newBlock()
820 self.compileJumpIf(node.test, elseblock, False)
821 self.visit(node.body)
822 self.emit("JUMP_FORWARD", endblock)
823 self.nextBlock(elseblock)
824 self.visit(node.orelse)
825 self.nextBlock(endblock)
826
827 def emitChainedCompareStep(self, op, value, cleanup, jump="JUMP_IF_FALSE_OR_POP"):
828 self.visit(value)
829 self.emit("DUP_TOP")
830 self.emit("ROT_THREE")
831 self.defaultEmitCompare(op)
832 self.emit(jump, cleanup)
833 self.nextBlock(label="compare_or_cleanup")
834
835 def defaultEmitCompare(self, op):
836 self.emit("COMPARE_OP", self._cmp_opcode[type(op)])
837
838 def visitCompare(self, node):
839 self.update_lineno(node)
840 self.visit(node.left)
841 cleanup = self.newBlock("cleanup")
842 for op, code in zip(node.ops[:-1], node.comparators[:-1]):
843 self.emitChainedCompareStep(op, code, cleanup)
844 # now do the last comparison
845 if node.ops:
846 op = node.ops[-1]
847 code = node.comparators[-1]
848 self.visit(code)
849 self.defaultEmitCompare(op)
850 if len(node.ops) > 1:
851 end = self.newBlock("end")
852 self.emit("JUMP_FORWARD", end)
853 self.nextBlock(cleanup)
854 self.emit("ROT_TWO")
855 self.emit("POP_TOP")
856 self.nextBlock(end)
857
858 def get_qual_prefix(self, gen):
859 prefix = ""
860 if gen.scope.global_scope:
861 return prefix
862 # Construct qualname prefix
863 parent = gen.scope.parent
864 while not isinstance(parent, symbols.ModuleScope):
865 # Only real functions use "<locals>", nested scopes like
866 # comprehensions don't.
867 if type(parent) in (symbols.FunctionScope, symbols.LambdaScope):
868 prefix = parent.name + ".<locals>." + prefix
869 else:
870 prefix = parent.name + "." + prefix
871 if parent.global_scope:
872 break
873 parent = parent.parent
874 return prefix
875
876 def _makeClosure(self, gen, flags):
877 prefix = ""
878 if not isinstance(gen.tree, ast.ClassDef):
879 prefix = self.get_qual_prefix(gen)
880
881 frees = gen.scope.get_free_vars()
882 if frees:
883 for name in frees:
884 self.emit("LOAD_CLOSURE", name)
885 self.emit("BUILD_TUPLE", len(frees))
886 flags |= 0x08
887
888 gen.set_qual_name(prefix + gen.name)
889 self.emit("LOAD_CONST", gen)
890 self.emit("LOAD_CONST", prefix + gen.name) # py3 qualname
891 self.emit("MAKE_FUNCTION", flags)
892
893 def visitDelete(self, node):
894 self.set_lineno(node)
895 self.visit(node.targets)
896
897 def compile_comprehension(self, node, name, elt, val, opcode, oparg=0):
898 node.args = self.conjure_arguments([ast.arg(".0", None)])
899 node.body = []
900 self.update_lineno(node)
901 gen = self.make_func_codegen(node, name, node.lineno)
902
903 if opcode:
904 gen.emit(opcode, oparg)
905
906 gen.compile_comprehension_body(node.generators, 0, elt, val, type(node), True)
907
908 if not isinstance(node, ast.GeneratorExp):
909 gen.emit("RETURN_VALUE")
910
911 gen.finishFunction()
912
913 self._makeClosure(gen, 0)
914
915 # precomputation of outmost iterable
916 self.visit(node.generators[0].iter)
917 if node.generators[0].is_async:
918 self.emit("GET_AITER")
919 self.emitAsyncIterYieldFrom()
920 else:
921 self.emit("GET_ITER")
922 self.emit("CALL_FUNCTION", 1)
923
924 if gen.scope.coroutine and type(node) is not ast.GeneratorExp:
925 self.emit("GET_AWAITABLE")
926 self.emit("LOAD_CONST", None)
927 self.emit("YIELD_FROM")
928
929 def visitGeneratorExp(self, node):
930 self.compile_comprehension(node, sys.intern("<genexpr>"), node.elt, None, None)
931
932 def visitListComp(self, node):
933 self.compile_comprehension(
934 node, sys.intern("<listcomp>"), node.elt, None, "BUILD_LIST"
935 )
936
937 def visitSetComp(self, node):
938 self.compile_comprehension(
939 node, sys.intern("<setcomp>"), node.elt, None, "BUILD_SET"
940 )
941
942 def visitDictComp(self, node):
943 self.compile_comprehension(
944 node, sys.intern("<dictcomp>"), node.key, node.value, "BUILD_MAP"
945 )
946
947 def compile_comprehension_body(self, generators, gen_index, elt, val, type, load_iter_from_dot0=False):
948 if generators[gen_index].is_async:
949 self.compile_async_comprehension(generators, gen_index, elt, val, type, load_iter_from_dot0)
950 else:
951 self.compile_sync_comprehension(generators, gen_index, elt, val, type, load_iter_from_dot0)
952
953 def compile_async_comprehension(self, generators, gen_index, elt, val, type,
954 load_iter_from_dot0):
955 try_ = self.newBlock("try")
956 after_try = self.newBlock("after_try")
957 except_ = self.newBlock("except")
958 if_cleanup = self.newBlock("if_cleanup")
959 try_cleanup = self.newBlock("try_cleanup")
960
961 gen = generators[gen_index]
962 if load_iter_from_dot0:
963 self.loadName(".0")
964 else:
965 self.visit(gen.iter)
966 self.emit("GET_AITER")
967 self.emitAsyncIterYieldFrom()
968
969 self.nextBlock(try_)
970 self.emit("SETUP_EXCEPT", except_)
971 self.setups.push((EXCEPT, try_))
972 self.emit("GET_ANEXT")
973 self.emit("LOAD_CONST", None)
974 self.emit("YIELD_FROM")
975 self.visit(gen.target)
976 self.emit("POP_BLOCK")
977 self.setups.pop()
978 self.emit("JUMP_FORWARD", after_try)
979
980 self.nextBlock(except_)
981 self.emit("DUP_TOP")
982 self.emit("LOAD_GLOBAL", "StopAsyncIteration")
983 self.emit("COMPARE_OP", "exception match")
984 self.emit("POP_JUMP_IF_TRUE", try_cleanup)
985 self.emit("END_FINALLY")
986
987 self.nextBlock(after_try)
988 for if_ in gen.ifs:
989 self.compileJumpIf(if_, if_cleanup, False)
990 self.newBlock()
991
992 gen_index += 1
993 if gen_index < len(generators):
994 self.compile_comprehension_body(generators, gen_index, elt, val, type)
995 elif type is ast.GeneratorExp:
996 self.visit(elt)
997 self.emit("YIELD_VALUE")
998 self.emit("POP_TOP")
999 elif type is ast.ListComp:
1000 self.visit(elt)
1001 self.emit("LIST_APPEND", gen_index + 1)
1002 elif type is ast.SetComp:
1003 self.visit(elt)
1004 self.emit("SET_ADD", gen_index + 1)
1005 elif type is ast.DictComp:
1006 self.compile_dictcomp_element(elt, val)
1007 self.emit("MAP_ADD", gen_index + 1)
1008 else:
1009 raise NotImplementedError("unknown comprehension type")
1010
1011 self.nextBlock(if_cleanup)
1012 self.emit("JUMP_ABSOLUTE", try_)
1013
1014 self.nextBlock(try_cleanup)
1015 self.emit("POP_TOP")
1016 self.emit("POP_TOP")
1017 self.emit("POP_TOP")
1018 self.emit("POP_EXCEPT") # for SETUP_EXCEPT
1019 self.emit("POP_TOP")
1020
1021 def compile_sync_comprehension(self, generators, gen_index, elt, val, type, load_iter_from_dot0):
1022 start = self.newBlock("start")
1023 skip = self.newBlock("skip")
1024 if_cleanup = self.newBlock("if_cleanup")
1025 anchor = self.newBlock("anchor")
1026
1027 gen = generators[gen_index]
1028 if load_iter_from_dot0:
1029 self.loadName(".0")
1030 else:
1031 self.visit(gen.iter)
1032 self.emit("GET_ITER")
1033
1034 self.nextBlock(start)
1035 self.emit("FOR_ITER", anchor)
1036 self.nextBlock()
1037 self.visit(gen.target)
1038
1039 for if_ in gen.ifs:
1040 self.compileJumpIf(if_, if_cleanup, False)
1041 self.newBlock()
1042
1043 gen_index += 1
1044 if gen_index < len(generators):
1045 self.compile_comprehension_body(generators, gen_index, elt, val, type)
1046 else:
1047 if type is ast.GeneratorExp:
1048 self.visit(elt)
1049 self.emit("YIELD_VALUE")
1050 self.emit("POP_TOP")
1051 elif type is ast.ListComp:
1052 self.visit(elt)
1053 self.emit("LIST_APPEND", gen_index + 1)
1054 elif type is ast.SetComp:
1055 self.visit(elt)
1056 self.emit("SET_ADD", gen_index + 1)
1057 elif type is ast.DictComp:
1058 self.compile_dictcomp_element(elt, val)
1059 self.emit("MAP_ADD", gen_index + 1)
1060 else:
1061 raise NotImplementedError("unknown comprehension type")
1062
1063 self.nextBlock(skip)
1064 self.nextBlock(if_cleanup)
1065 self.emit("JUMP_ABSOLUTE", start)
1066 self.nextBlock(anchor)
1067
1068 def compile_dictcomp_element(self, elt, val):
1069 self.visit(val)
1070 self.visit(elt)
1071
1072 # exception related
1073
1074 def visitAssert(self, node):
1075 # XXX would be interesting to implement this via a
1076 # transformation of the AST before this stage
1077 if not self.optimization_lvl:
1078 end = self.newBlock()
1079 self.set_lineno(node)
1080 # XXX AssertionError appears to be special case -- it is always
1081 # loaded as a global even if there is a local name. I guess this
1082 # is a sort of renaming op.
1083 self.nextBlock()
1084 self.compileJumpIf(node.test, end, True)
1085
1086 self.nextBlock()
1087 self.emit("LOAD_GLOBAL", "AssertionError")
1088 if node.msg:
1089 self.visit(node.msg)
1090 self.emit("CALL_FUNCTION", 1)
1091 self.emit("RAISE_VARARGS", 1)
1092 else:
1093 self.emit("RAISE_VARARGS", 1)
1094 self.nextBlock(end)
1095
1096 def visitRaise(self, node):
1097 self.set_lineno(node)
1098 n = 0
1099 if node.exc:
1100 self.visit(node.exc)
1101 n = n + 1
1102 if node.cause:
1103 self.visit(node.cause)
1104 n = n + 1
1105 self.emit("RAISE_VARARGS", n)
1106
1107 def visitTry(self, node):
1108 self.set_lineno(node)
1109 if node.finalbody:
1110 if node.handlers:
1111 self.emit_try_finally(
1112 node,
1113 lambda: self.visitTryExcept(node),
1114 lambda: self.visit(node.finalbody),
1115 )
1116 else:
1117 self.emit_try_finally(
1118 node,
1119 lambda: self.visit(node.body),
1120 lambda: self.visit(node.finalbody),
1121 )
1122 return
1123
1124 self.visitTryExcept(node)
1125
1126 def visitTryExcept(self, node):
1127 body = self.newBlock("try_body")
1128 handlers = self.newBlock("try_handlers")
1129 end = self.newBlock("try_end")
1130 if node.orelse:
1131 lElse = self.newBlock("try_else")
1132 else:
1133 lElse = end
1134
1135 self.emit("SETUP_EXCEPT", handlers)
1136 self.nextBlock(body)
1137 self.setups.push((EXCEPT, body))
1138 self.visit(node.body)
1139 self.emit("POP_BLOCK")
1140 self.setups.pop()
1141 self.emit("JUMP_FORWARD", lElse)
1142 self.nextBlock(handlers)
1143
1144 last = len(node.handlers) - 1
1145 for i in range(len(node.handlers)):
1146 handler = node.handlers[i]
1147 expr = handler.type
1148 target = handler.name
1149 body = handler.body
1150 self.set_lineno(handler)
1151 if expr:
1152 self.emit("DUP_TOP")
1153 self.visit(expr)
1154 self.emit("COMPARE_OP", "exception match")
1155 next = self.newBlock()
1156 self.emit("POP_JUMP_IF_FALSE", next)
1157 self.nextBlock()
1158 elif i < last:
1159 raise SyntaxError(
1160 "default 'except:' must be last",
1161 self.syntax_error_position(handler),
1162 )
1163 else:
1164 self.set_lineno(handler)
1165 self.emit("POP_TOP")
1166 self.emit_except_local(handler)
1167
1168 if target:
1169
1170 def clear_name():
1171 self.emit("LOAD_CONST", None)
1172 self.storeName(target)
1173 self.delName(target)
1174
1175 self.emit_try_finally(node, lambda: self.visit(body), clear_name, True)
1176 else:
1177 # "block" param shouldn't matter, so just pass None
1178 self.setups.push((EXCEPT, None))
1179 self.visit(body)
1180 self.emit("POP_EXCEPT")
1181 self.setups.pop()
1182
1183 self.emit("JUMP_FORWARD", end)
1184 if expr:
1185 self.nextBlock(next)
1186 else:
1187 self.nextBlock(label="handler_end")
1188 self.emit("END_FINALLY")
1189 if node.orelse:
1190 self.nextBlock(lElse)
1191 self.visit(node.orelse)
1192 self.nextBlock(end)
1193
1194 def emit_except_local(self, handler: ast.ExceptHandler):
1195 target = handler.name
1196 if target:
1197 self.update_lineno(handler.type)
1198 self.storeName(target)
1199 else:
1200 self.emit("POP_TOP")
1201 self.emit("POP_TOP")
1202
1203 def emit_try_finally(self, node, try_body, finalbody, except_protect=False):
1204 raise NotImplementedError("missing overridde")
1205
1206 def visitWith(self, node):
1207 self.set_lineno(node)
1208 body = self.newBlock()
1209 stack = []
1210 for withitem in node.items:
1211 final = self.newBlock()
1212 stack.append(final)
1213 self.__with_count += 1
1214 self.visit(withitem.context_expr)
1215
1216 self.emit("SETUP_WITH", final)
1217
1218 if withitem.optional_vars is None:
1219 self.emit("POP_TOP")
1220 else:
1221 self.visit(withitem.optional_vars)
1222
1223 self.setups.push((TRY_FINALLY, body))
1224
1225 self.nextBlock(body)
1226 self.visit(node.body)
1227
1228 while stack:
1229 final = stack.pop()
1230 self.emit("POP_BLOCK")
1231 self.setups.pop()
1232 self.emit("LOAD_CONST", None)
1233 self.nextBlock(final)
1234 self.setups.push((END_FINALLY, final))
1235 self.emit("WITH_CLEANUP_START")
1236 self.emit("WITH_CLEANUP_FINISH")
1237 self.emit("END_FINALLY")
1238 self.setups.pop()
1239 self.__with_count -= 1
1240
1241 def visitAsyncWith(self, node):
1242 self.set_lineno(node)
1243 body = self.newBlock()
1244 stack = []
1245 for withitem in node.items:
1246 final = self.newBlock()
1247 stack.append(final)
1248 self.__with_count += 1
1249 self.visit(withitem.context_expr)
1250
1251 self.emit("BEFORE_ASYNC_WITH")
1252 self.emit("GET_AWAITABLE")
1253 self.emit("LOAD_CONST", None)
1254 self.emit("YIELD_FROM")
1255 self.emit("SETUP_ASYNC_WITH", final)
1256
1257 if withitem.optional_vars is None:
1258 self.emit("POP_TOP")
1259 else:
1260 self.visit(withitem.optional_vars)
1261
1262 self.setups.push((TRY_FINALLY, body))
1263
1264 self.nextBlock(body)
1265 self.visit(node.body)
1266
1267 while stack:
1268 final = stack.pop()
1269 self.emit("POP_BLOCK")
1270 self.setups.pop()
1271 self.emit("LOAD_CONST", None)
1272 self.nextBlock(final)
1273 self.setups.push((END_FINALLY, final))
1274 self.emit("WITH_CLEANUP_START")
1275 self.emit("GET_AWAITABLE")
1276 self.emit("LOAD_CONST", None)
1277 self.emit("YIELD_FROM")
1278 self.emit("WITH_CLEANUP_FINISH")
1279 self.emit("END_FINALLY")
1280 self.setups.pop()
1281 self.__with_count -= 1
1282
1283 # misc
1284
1285 def visitExpr(self, node):
1286 self.set_lineno(node)
1287 # CPy3.6 discards lots of constants
1288 if self.interactive:
1289 self.visit(node.value)
1290 self.emit("PRINT_EXPR")
1291 elif not is_const(node.value):
1292 self.visit(node.value)
1293 self.emit("POP_TOP")
1294
1295 def visitNum(self, node):
1296 self.update_lineno(node)
1297 self.emit("LOAD_CONST", node.n)
1298
1299 def visitStr(self, node):
1300 self.update_lineno(node)
1301 self.emit("LOAD_CONST", node.s)
1302
1303 def visitBytes(self, node):
1304 self.update_lineno(node)
1305 self.emit("LOAD_CONST", node.s)
1306
1307 def visitNameConstant(self, node):
1308 self.update_lineno(node)
1309 self.emit("LOAD_CONST", node.value)
1310
1311 def visitConst(self, node):
1312 self.update_lineno(node)
1313 self.emit("LOAD_CONST", node.value)
1314
1315 def visitKeyword(self, node):
1316 self.emit("LOAD_CONST", node.name)
1317 self.visit(node.expr)
1318
1319 def visitGlobal(self, node):
1320 self.set_lineno(node)
1321 # no code to generate
1322
1323 def visitNonlocal(self, node):
1324 self.set_lineno(node)
1325 # no code to generate
1326
1327 def visitName(self, node):
1328 self.update_lineno(node)
1329 if isinstance(node.ctx, ast.Store):
1330 self.storeName(node.id)
1331 elif isinstance(node.ctx, ast.Del):
1332 self.delName(node.id)
1333 elif node.id == "__debug__":
1334 self.emit("LOAD_CONST", not bool(self.optimization_lvl))
1335 else:
1336 self.loadName(node.id)
1337
1338 def visitPass(self, node):
1339 self.set_lineno(node)
1340
1341 def visitImport(self, node):
1342 self.set_lineno(node)
1343 level = 0
1344 for alias in node.names:
1345 name = alias.name
1346 asname = alias.asname
1347 self.emit("LOAD_CONST", level)
1348 self.emit("LOAD_CONST", None)
1349 self.emit("IMPORT_NAME", self.mangle(name))
1350 mod = name.split(".")[0]
1351 if asname:
1352 self.emitImportAs(name, asname)
1353 else:
1354 self.storeName(mod)
1355
1356 def visitImportFrom(self, node):
1357 self.set_lineno(node)
1358 level = node.level
1359 fromlist = tuple(alias.name for alias in node.names)
1360 self.emit("LOAD_CONST", level)
1361 self.emit("LOAD_CONST", fromlist)
1362 self.emit("IMPORT_NAME", node.module or "")
1363 for alias in node.names:
1364 name = alias.name
1365 asname = alias.asname
1366 if name == "*":
1367 self.namespace = 0
1368 self.emit("IMPORT_STAR")
1369 # There can only be one name w/ from ... import *
1370 assert len(node.names) == 1
1371 return
1372 else:
1373 self.emit("IMPORT_FROM", name)
1374 self.storeName(asname or name)
1375 self.emit("POP_TOP")
1376
1377 def emitImportAs(self, name: str, asname: str):
1378 elts = name.split(".")
1379 if len(elts) == 1:
1380 self.storeName(asname)
1381 return
1382 for elt in elts[1:]:
1383 self.emit("LOAD_ATTR", elt)
1384 self.storeName(asname)
1385
1386 def visitAttribute(self, node):
1387 self.update_lineno(node)
1388 self.visit(node.value)
1389 if isinstance(node.ctx, ast.Store):
1390 self.emit("STORE_ATTR", self.mangle(node.attr))
1391 elif isinstance(node.ctx, ast.Del):
1392 self.emit("DELETE_ATTR", self.mangle(node.attr))
1393 else:
1394 self.emit("LOAD_ATTR", self.mangle(node.attr))
1395
1396 # next five implement assignments
1397
1398 def visitAssign(self, node):
1399 self.set_lineno(node)
1400 self.visit(node.value)
1401 dups = len(node.targets) - 1
1402 for i in range(len(node.targets)):
1403 elt = node.targets[i]
1404 if i < dups:
1405 self.emit("DUP_TOP")
1406 if isinstance(elt, ast.AST):
1407 self.visit(elt)
1408
1409 def checkAnnExpr(self, node):
1410 self._visitAnnotation(node)
1411 self.emit("POP_TOP")
1412
1413 def checkAnnSlice(self, node):
1414 if isinstance(node, ast.Index):
1415 self.checkAnnExpr(node.value)
1416 else:
1417 if node.lower:
1418 self.checkAnnExpr(node.lower)
1419 if node.upper:
1420 self.checkAnnExpr(node.upper)
1421 if node.step:
1422 self.checkAnnExpr(node.step)
1423
1424 def checkAnnSubscr(self, node):
1425 if isinstance(node, (ast.Index, ast.Slice)):
1426 self.checkAnnSlice(node)
1427 elif isinstance(node, ast.ExtSlice):
1428 for v in node.dims:
1429 self.checkAnnSlice(v)
1430
1431 def checkAnnotation(self, node):
1432 if isinstance(self.tree, (ast.Module, ast.ClassDef)):
1433 self.checkAnnExpr(node.annotation)
1434
1435 def findAnn(self, stmts):
1436 for stmt in stmts:
1437 if isinstance(stmt, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
1438 # Don't recurse into definitions looking for annotations
1439 continue
1440 elif isinstance(stmt, ast.AnnAssign):
1441 return True
1442 elif isinstance(stmt, (ast.stmt, ast.ExceptHandler)):
1443 for field in stmt._fields:
1444 child = getattr(stmt, field)
1445 if isinstance(child, list):
1446 if self.findAnn(child):
1447 return True
1448
1449 return False
1450
1451 def emitStoreAnnotation(self, name: str, annotation: ast.expr):
1452 assert self.did_setup_annotations
1453 self._visitAnnotation(annotation)
1454 mangled = self.mangle(name)
1455 self.emit("STORE_ANNOTATION", mangled)
1456
1457 def visitAnnAssign(self, node):
1458 self.set_lineno(node)
1459 if node.value:
1460 self.visit(node.value)
1461 self.visit(node.target)
1462 if isinstance(node.target, ast.Name):
1463 # If we have a simple name in a module or class, store the annotation
1464 if node.simple and isinstance(self.tree, (ast.Module, ast.ClassDef)):
1465 self.emitStoreAnnotation(node.target.id, node.annotation)
1466 elif isinstance(node.target, ast.Attribute):
1467 if not node.value:
1468 self.checkAnnExpr(node.target.value)
1469 elif isinstance(node.target, ast.Subscript):
1470 if not node.value:
1471 self.checkAnnExpr(node.target.value)
1472 self.checkAnnSubscr(node.target.slice)
1473 else:
1474 raise SystemError(
1475 f"invalid node type {type(node).__name__} for annotated assignment"
1476 )
1477
1478 if not node.simple:
1479 self.checkAnnotation(node)
1480
1481 def visitAssName(self, node):
1482 if node.flags == "OP_ASSIGN":
1483 self.storeName(node.name)
1484 elif node.flags == "OP_DELETE":
1485 self.set_lineno(node)
1486 self.delName(node.name)
1487 else:
1488 print("oops", node.flags)
1489 assert 0
1490
1491 def visitAssAttr(self, node):
1492 self.visit(node.expr)
1493 if node.flags == "OP_ASSIGN":
1494 self.emit("STORE_ATTR", self.mangle(node.attrname))
1495 elif node.flags == "OP_DELETE":
1496 self.emit("DELETE_ATTR", self.mangle(node.attrname))
1497 else:
1498 print("warning: unexpected flags:", node.flags)
1499 print(node)
1500 assert 0
1501
1502 def _visitAssSequence(self, node, op="UNPACK_SEQUENCE"):
1503 if findOp(node) != "OP_DELETE":
1504 self.emit(op, len(node.nodes))
1505 for child in node.nodes:
1506 self.visit(child)
1507
1508 visitAssTuple = _visitAssSequence
1509 visitAssList = _visitAssSequence
1510
1511 # augmented assignment
1512
1513 def visitAugAssign(self, node):
1514 self.set_lineno(node)
1515 aug_node = wrap_aug(node.target)
1516 self.visit(aug_node, "load")
1517 self.visit(node.value)
1518 self.emit(self._augmented_opcode[type(node.op)])
1519 self.visit(aug_node, "store")
1520
1521 _augmented_opcode = {
1522 ast.Add: "INPLACE_ADD",
1523 ast.Sub: "INPLACE_SUBTRACT",
1524 ast.Mult: "INPLACE_MULTIPLY",
1525 ast.MatMult: "INPLACE_MATRIX_MULTIPLY",
1526 ast.Div: "INPLACE_TRUE_DIVIDE",
1527 ast.FloorDiv: "INPLACE_FLOOR_DIVIDE",
1528 ast.Mod: "INPLACE_MODULO",
1529 ast.Pow: "INPLACE_POWER",
1530 ast.RShift: "INPLACE_RSHIFT",
1531 ast.LShift: "INPLACE_LSHIFT",
1532 ast.BitAnd: "INPLACE_AND",
1533 ast.BitXor: "INPLACE_XOR",
1534 ast.BitOr: "INPLACE_OR",
1535 }
1536
1537 def visitAugName(self, node, mode):
1538 if mode == "load":
1539 self.loadName(node.id)
1540 elif mode == "store":
1541 self.storeName(node.id)
1542
1543 def visitAugAttribute(self, node, mode):
1544 if mode == "load":
1545 self.visit(node.value)
1546 self.emit("DUP_TOP")
1547 self.emit("LOAD_ATTR", self.mangle(node.attr))
1548 elif mode == "store":
1549 self.emit("ROT_TWO")
1550 self.emit("STORE_ATTR", self.mangle(node.attr))
1551
1552 def visitAugSubscript(self, node, mode):
1553 if mode == "load":
1554 self.visitSubscript(node, 1)
1555 elif mode == "store":
1556 self.emit("ROT_THREE")
1557 self.emit("STORE_SUBSCR")
1558
1559 def visitExec(self, node):
1560 self.visit(node.expr)
1561 if node.locals is None:
1562 self.emit("LOAD_CONST", None)
1563 else:
1564 self.visit(node.locals)
1565 if node.globals is None:
1566 self.emit("DUP_TOP")
1567 else:
1568 self.visit(node.globals)
1569 self.emit("EXEC_STMT")
1570
1571 def compiler_subkwargs(self, kwargs, begin, end):
1572 nkwargs = end - begin
1573 if nkwargs > 1:
1574 for i in range(begin, end):
1575 self.visit(kwargs[i].value)
1576 self.emit("LOAD_CONST", tuple(arg.arg for arg in kwargs[begin:end]))
1577 self.emit("BUILD_CONST_KEY_MAP", nkwargs)
1578 else:
1579 for i in range(begin, end):
1580 self.emit("LOAD_CONST", kwargs[i].arg)
1581 self.visit(kwargs[i].value)
1582 self.emit("BUILD_MAP", nkwargs)
1583
1584 def _call_helper(self, argcnt, args, kwargs):
1585 mustdictunpack = any(arg.arg is None for arg in kwargs)
1586 nelts = len(args)
1587 nkwelts = len(kwargs)
1588 # the number of tuples and dictionaries on the stack
1589 nsubkwargs = nsubargs = 0
1590 nseen = argcnt # the number of positional arguments on the stack
1591 for arg in args:
1592 if isinstance(arg, ast.Starred):
1593 if nseen:
1594 self.emit("BUILD_TUPLE", nseen)
1595 nseen = 0
1596 nsubargs += 1
1597 self.visit(arg.value)
1598 nsubargs += 1
1599 else:
1600 self.visit(arg)
1601 nseen += 1
1602
1603 if nsubargs or mustdictunpack:
1604 if nseen:
1605 self.emit("BUILD_TUPLE", nseen)
1606 nsubargs += 1
1607 if nsubargs > 1:
1608 self.emit("BUILD_TUPLE_UNPACK_WITH_CALL", nsubargs)
1609 elif nsubargs == 0:
1610 self.emit("BUILD_TUPLE", 0)
1611
1612 nseen = 0 # the number of keyword arguments on the stack following
1613 for i, kw in enumerate(kwargs):
1614 if kw.arg is None:
1615 if nseen:
1616 # A keyword argument unpacking.
1617 self.compiler_subkwargs(kwargs, i - nseen, i)
1618 nsubkwargs += 1
1619 nseen = 0
1620 self.visit(kw.value)
1621 nsubkwargs += 1
1622 else:
1623 nseen += 1
1624 if nseen:
1625 self.compiler_subkwargs(kwargs, nkwelts - nseen, nkwelts)
1626 nsubkwargs += 1
1627 if nsubkwargs > 1:
1628 self.emit("BUILD_MAP_UNPACK_WITH_CALL", nsubkwargs)
1629 self.emit("CALL_FUNCTION_EX", int(nsubkwargs > 0))
1630 elif nkwelts:
1631 for kw in kwargs:
1632 self.visit(kw.value)
1633 self.emit("LOAD_CONST", tuple(arg.arg for arg in kwargs))
1634 self.emit("CALL_FUNCTION_KW", nelts + nkwelts + argcnt)
1635 else:
1636 self.emit("CALL_FUNCTION", nelts + argcnt)
1637
1638 def visitCall(self, node):
1639 self.update_lineno(node)
1640 self.visit(node.func)
1641 self._call_helper(0, node.args, node.keywords)
1642
1643 def visitPrint(self, node, newline=0):
1644 self.set_lineno(node)
1645 if node.dest:
1646 self.visit(node.dest)
1647 for child in node.nodes:
1648 if node.dest:
1649 self.emit("DUP_TOP")
1650 self.visit(child)
1651 if node.dest:
1652 self.emit("ROT_TWO")
1653 self.emit("PRINT_ITEM_TO")
1654 else:
1655 self.emit("PRINT_ITEM")
1656 if node.dest and not newline:
1657 self.emit("POP_TOP")
1658
1659 def visitPrintnl(self, node):
1660 self.visitPrint(node, newline=1)
1661 if node.dest:
1662 self.emit("PRINT_NEWLINE_TO")
1663 else:
1664 self.emit("PRINT_NEWLINE")
1665
1666 def checkReturn(self, node):
1667 if not isinstance(self.tree, (ast.FunctionDef, ast.AsyncFunctionDef)):
1668 raise SyntaxError(
1669 "'return' outside function", self.syntax_error_position(node)
1670 )
1671 elif self.scope.coroutine and self.scope.generator and node.value:
1672 raise SyntaxError(
1673 "'return' with value in async generator",
1674 self.syntax_error_position(node),
1675 )
1676
1677 def visitReturn(self, node):
1678 self.checkReturn(node)
1679 self.set_lineno(node)
1680 if node.value:
1681 self.visit(node.value)
1682 else:
1683 self.emit("LOAD_CONST", None)
1684 self.emit("RETURN_VALUE")
1685
1686 def visitYield(self, node):
1687 if not isinstance(
1688 self.tree,
1689 (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda, ast.GeneratorExp),
1690 ):
1691 raise SyntaxError(
1692 "'yield' outside function", self.syntax_error_position(node)
1693 )
1694 self.update_lineno(node)
1695 if node.value:
1696 self.visit(node.value)
1697 else:
1698 self.emit("LOAD_CONST", None)
1699 self.emit("YIELD_VALUE")
1700
1701 def visitYieldFrom(self, node):
1702 if not isinstance(
1703 self.tree,
1704 (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda, ast.GeneratorExp),
1705 ):
1706 raise SyntaxError(
1707 "'yield' outside function", self.syntax_error_position(node)
1708 )
1709 elif self.scope.coroutine:
1710 raise SyntaxError(
1711 "'yield from' inside async function", self.syntax_error_position(node)
1712 )
1713
1714 self.update_lineno(node)
1715 self.visit(node.value)
1716 self.emit("GET_YIELD_FROM_ITER")
1717 self.emit("LOAD_CONST", None)
1718 self.emit("YIELD_FROM")
1719
1720 def visitAwait(self, node):
1721 self.update_lineno(node)
1722 self.visit(node.value)
1723 self.emit("GET_AWAITABLE")
1724 self.emit("LOAD_CONST", None)
1725 self.emit("YIELD_FROM")
1726
1727 # slice and subscript stuff
1728 def visitSubscript(self, node, aug_flag=None):
1729 self.update_lineno(node)
1730 self.visit(node.value)
1731 self.visit(node.slice)
1732 if isinstance(node.ctx, ast.Load):
1733 self.emit("BINARY_SUBSCR")
1734 elif isinstance(node.ctx, ast.Store):
1735 if aug_flag:
1736 self.emit("DUP_TOP_TWO")
1737 self.emit("BINARY_SUBSCR")
1738 else:
1739 self.emit("STORE_SUBSCR")
1740 elif isinstance(node.ctx, ast.Del):
1741 self.emit("DELETE_SUBSCR")
1742 else:
1743 assert 0
1744
1745 # binary ops
1746
1747 def binaryOp(self, node, op):
1748 self.visit(node.left)
1749 self.visit(node.right)
1750 self.emit(op)
1751
1752 _binary_opcode = {
1753 ast.Add: "BINARY_ADD",
1754 ast.Sub: "BINARY_SUBTRACT",
1755 ast.Mult: "BINARY_MULTIPLY",
1756 ast.MatMult: "BINARY_MATRIX_MULTIPLY",
1757 ast.Div: "BINARY_TRUE_DIVIDE",
1758 ast.FloorDiv: "BINARY_FLOOR_DIVIDE",
1759 ast.Mod: "BINARY_MODULO",
1760 ast.Pow: "BINARY_POWER",
1761 ast.LShift: "BINARY_LSHIFT",
1762 ast.RShift: "BINARY_RSHIFT",
1763 ast.BitOr: "BINARY_OR",
1764 ast.BitXor: "BINARY_XOR",
1765 ast.BitAnd: "BINARY_AND",
1766 }
1767
1768 def visitBinOp(self, node):
1769 self.update_lineno(node)
1770 self.visit(node.left)
1771 self.visit(node.right)
1772 op = self._binary_opcode[type(node.op)]
1773 self.emit(op)
1774
1775 # unary ops
1776
1777 def unaryOp(self, node, op):
1778 self.visit(node.operand)
1779 self.emit(op)
1780
1781 _unary_opcode = {
1782 ast.Invert: "UNARY_INVERT",
1783 ast.USub: "UNARY_NEGATIVE",
1784 ast.UAdd: "UNARY_POSITIVE",
1785 ast.Not: "UNARY_NOT",
1786 }
1787
1788 def visitUnaryOp(self, node):
1789 self.update_lineno(node)
1790 self.unaryOp(node, self._unary_opcode[type(node.op)])
1791
1792 def visitBackquote(self, node):
1793 return self.unaryOp(node, "UNARY_CONVERT")
1794
1795 # object constructors
1796
1797 def visitEllipsis(self, node):
1798 self.update_lineno(node)
1799 self.emit("LOAD_CONST", Ellipsis)
1800
1801 def _visitUnpack(self, node):
1802 before = 0
1803 after = 0
1804 starred = None
1805 for elt in node.elts:
1806 if isinstance(elt, ast.Starred):
1807 if starred is not None:
1808 raise SyntaxError(
1809 "two starred expressions in assignment",
1810 self.syntax_error_position(elt),
1811 )
1812 elif before >= 256 or len(node.elts) - before - 1 >= (1 << 31) >> 8:
1813 raise SyntaxError(
1814 "too many expressions in star-unpacking assignment",
1815 self.syntax_error_position(elt),
1816 )
1817 starred = elt.value
1818 elif starred:
1819 after += 1
1820 else:
1821 before += 1
1822 if starred:
1823 self.emit("UNPACK_EX", after << 8 | before)
1824 else:
1825 self.emit("UNPACK_SEQUENCE", before)
1826
1827 def hasStarred(self, elts):
1828 for elt in elts:
1829 if isinstance(elt, ast.Starred):
1830 return True
1831 return False
1832
1833 def _visitSequence(self, node, build_op, build_inner_op, build_ex_op, ctx):
1834 self.update_lineno(node)
1835 if isinstance(ctx, ast.Store):
1836 self._visitUnpack(node)
1837 starred_load = False
1838 else:
1839 starred_load = self.hasStarred(node.elts)
1840
1841 chunks = 0
1842 in_chunk = 0
1843
1844 def out_chunk():
1845 nonlocal chunks, in_chunk
1846 if in_chunk:
1847 self.emit(build_inner_op, in_chunk)
1848 in_chunk = 0
1849 chunks += 1
1850
1851 for elt in node.elts:
1852 if starred_load:
1853 if isinstance(elt, ast.Starred):
1854 out_chunk()
1855 chunks += 1
1856 else:
1857 in_chunk += 1
1858
1859 if isinstance(elt, ast.Starred):
1860 self.visit(elt.value)
1861 else:
1862 self.visit(elt)
1863 # Output trailing chunk, if any
1864 out_chunk()
1865
1866 if isinstance(ctx, ast.Load):
1867 if starred_load:
1868 self.emit(build_ex_op, chunks)
1869 else:
1870 self.emit(build_op, len(node.elts))
1871
1872 def visitStarred(self, node):
1873 if isinstance(node.ctx, ast.Store):
1874 raise SyntaxError(
1875 "starred assignment target must be in a list or tuple",
1876 self.syntax_error_position(node),
1877 )
1878 else:
1879 raise SyntaxError(
1880 "can't use starred expression here", self.syntax_error_position(node)
1881 )
1882
1883 def visitTuple(self, node):
1884 self._visitSequence(
1885 node, "BUILD_TUPLE", "BUILD_TUPLE", "BUILD_TUPLE_UNPACK", node.ctx
1886 )
1887
1888 def visitList(self, node):
1889 self._visitSequence(
1890 node, "BUILD_LIST", "BUILD_TUPLE", "BUILD_LIST_UNPACK", node.ctx
1891 )
1892
1893 def visitSet(self, node):
1894 self._visitSequence(
1895 node, "BUILD_SET", "BUILD_SET", "BUILD_SET_UNPACK", ast.Load()
1896 )
1897
1898 def visitSlice(self, node):
1899 num = 2
1900 if node.lower:
1901 self.visit(node.lower)
1902 else:
1903 self.emit("LOAD_CONST", None)
1904 if node.upper:
1905 self.visit(node.upper)
1906 else:
1907 self.emit("LOAD_CONST", None)
1908 if node.step:
1909 self.visit(node.step)
1910 num += 1
1911 self.emit("BUILD_SLICE", num)
1912
1913 def visitExtSlice(self, node):
1914 for d in node.dims:
1915 self.visit(d)
1916 self.emit("BUILD_TUPLE", len(node.dims))
1917
1918 # Create dict item by item. Saves interp stack size at the expense
1919 # of bytecode size/speed.
1920 def visitDict_by_one(self, node):
1921 self.update_lineno(node)
1922 self.emit("BUILD_MAP", 0)
1923 for k, v in zip(node.keys, node.values):
1924 self.emit("DUP_TOP")
1925 self.visit(k)
1926 self.visit(v)
1927 self.emit("ROT_THREE")
1928 self.emit("STORE_SUBSCR")
1929
1930 def _const_value(self, node):
1931 if isinstance(node, (ast.NameConstant, ast.Constant)):
1932 return node.value
1933 elif isinstance(node, ast.Num):
1934 return node.n
1935 elif isinstance(node, (ast.Str, ast.Bytes)):
1936 return node.s
1937 elif isinstance(node, ast.Ellipsis):
1938 return ...
1939 else:
1940 assert isinstance(node, ast.Name) and node.id == "__debug__"
1941 return not self.optimized
1942
1943 def get_bool_const(self, node):
1944 """Return True if node represent constantly true value, False if
1945 constantly false value, and None otherwise (non-constant)."""
1946 if isinstance(node, ast.Num):
1947 return bool(node.n)
1948 if isinstance(node, ast.NameConstant):
1949 return bool(node.value)
1950 if isinstance(node, ast.Str):
1951 return bool(node.s)
1952 if isinstance(node, ast.Name):
1953 if node.id == "__debug__":
1954 return not bool(self.optimization_lvl)
1955 if isinstance(node, ast.Constant):
1956 return bool(node.value)
1957
1958 def compile_subdict(self, node, begin, end):
1959 n = end - begin
1960 if n > 1 and all_items_const(node.keys, begin, end):
1961 for i in range(begin, end):
1962 self.visit(node.values[i])
1963
1964 self.emit(
1965 "LOAD_CONST", tuple(self._const_value(x) for x in node.keys[begin:end])
1966 )
1967 self.emit("BUILD_CONST_KEY_MAP", n)
1968 else:
1969 for i in range(begin, end):
1970 self.visit(node.keys[i])
1971 self.visit(node.values[i])
1972
1973 self.emit("BUILD_MAP", n)
1974
1975 def visitDict(self, node):
1976 self.update_lineno(node)
1977 containers = elements = 0
1978 is_unpacking = False
1979
1980 for i, (k, v) in enumerate(zip(node.keys, node.values)):
1981 is_unpacking = k is None
1982 if elements == 0xFFFF or (elements and is_unpacking):
1983 self.compile_subdict(node, i - elements, i)
1984 containers += 1
1985 elements = 0
1986
1987 if is_unpacking:
1988 self.visit(v)
1989 containers += 1
1990 else:
1991 elements += 1
1992
1993 if elements or containers == 0:
1994 self.compile_subdict(node, len(node.keys) - elements, len(node.keys))
1995 containers += 1
1996
1997 self.emitMapUnpack(containers, is_unpacking)
1998
1999 def emitMapUnpack(self, containers, is_unpacking):
2000 while containers > 1 or is_unpacking:
2001 oparg = min(containers, 255)
2002 self.emit("BUILD_MAP_UNPACK", oparg)
2003 containers -= oparg - 1
2004 is_unpacking = False
2005
2006 @property
2007 def name(self):
2008 if isinstance(self.tree, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)):
2009 return self.tree.name
2010 elif isinstance(self.tree, ast.SetComp):
2011 return "<setcomp>"
2012 elif isinstance(self.tree, ast.ListComp):
2013 return "<listcomp>"
2014 elif isinstance(self.tree, ast.DictComp):
2015 return "<dictcomp>"
2016 elif isinstance(self.tree, ast.GeneratorExp):
2017 return "<genexpr>"
2018 elif isinstance(self.tree, ast.Lambda):
2019 return "<lambda>"
2020
2021 def finishFunction(self):
2022 if self.graph.current.returns:
2023 return
2024 self.graph.startExitBlock()
2025 if not isinstance(self.tree, ast.Lambda):
2026 self.emit("LOAD_CONST", None)
2027 self.emit("RETURN_VALUE")
2028
2029 def make_child_codegen(
2030 self,
2031 tree: AST,
2032 graph: PyFlowGraph,
2033 codegen_type: Optional[Type[CinderCodeGenerator]] = None,
2034 ) -> CodeGenerator:
2035 if codegen_type is None:
2036 codegen_type = type(self)
2037 return codegen_type(self, tree, self.symbols, graph, self.optimization_lvl)
2038
2039 def make_func_codegen(
2040 self, func: AST, name: str, first_lineno: int
2041 ) -> CodeGenerator:
2042 filename = self.graph.filename
2043 symbols = self.symbols
2044 class_name = self.class_name
2045
2046 graph = self.make_function_graph(
2047 func, filename, symbols.scopes, class_name, name, first_lineno
2048 )
2049 res = self.make_child_codegen(func, graph)
2050 res.optimized = 1
2051 res.class_name = class_name
2052 return res
2053
2054 def make_class_codegen(
2055 self, klass: ast.ClassDef, first_lineno: int
2056 ) -> CodeGenerator:
2057 filename = self.graph.filename
2058 symbols = self.symbols
2059
2060 scope = symbols.scopes[klass]
2061 graph = self.flow_graph(
2062 klass.name,
2063 filename,
2064 scope,
2065 optimized=0,
2066 klass=True,
2067 docstring=self.get_docstring(klass),
2068 firstline=first_lineno,
2069 peephole_enabled=self.graph.peephole_enabled,
2070 )
2071
2072 res = self.make_child_codegen(klass, graph)
2073 res.class_name = klass.name
2074 return res
2075
2076 def make_function_graph(
2077 self, func, filename: str, scopes, class_name: str, name: str, first_lineno: int
2078 ) -> PyFlowGraph:
2079 args = [misc.mangle(elt.arg, class_name) for elt in func.args.args]
2080 kwonlyargs = [misc.mangle(elt.arg, class_name) for elt in func.args.kwonlyargs]
2081
2082 starargs = []
2083 if func.args.vararg:
2084 starargs.append(func.args.vararg.arg)
2085 if func.args.kwarg:
2086 starargs.append(func.args.kwarg.arg)
2087
2088 scope = scopes[func]
2089 graph = self.flow_graph(
2090 name,
2091 filename,
2092 scope,
2093 flags=self.get_graph_flags(func, scope),
2094 args=args,
2095 kwonlyargs=kwonlyargs,
2096 starargs=starargs,
2097 optimized=1,
2098 docstring=self.get_docstring(func),
2099 firstline=first_lineno,
2100 peephole_enabled=self.graph.peephole_enabled,
2101 )
2102
2103 return graph
2104
2105 def get_docstring(self, func) -> Optional[str]:
2106 doc = None
2107 isLambda = isinstance(func, ast.Lambda)
2108 if not isLambda and not self.strip_docstrings:
2109 doc = get_docstring(func)
2110 return doc
2111
2112 def get_graph_flags(self, func, scope):
2113 flags = 0
2114
2115 if func.args.vararg:
2116 flags = flags | CO_VARARGS
2117 if func.args.kwarg:
2118 flags = flags | CO_VARKEYWORDS
2119 if scope.nested:
2120 flags = flags | CO_NESTED
2121 if scope.generator and not scope.coroutine:
2122 flags = flags | CO_GENERATOR
2123 if not scope.generator and scope.coroutine:
2124 flags = flags | CO_COROUTINE
2125 if scope.generator and scope.coroutine:
2126 flags = flags | CO_ASYNC_GENERATOR
2127
2128 return flags
2129
2130 @classmethod
2131 def make_code_gen(
2132 cls,
2133 name: str,
2134 tree: AST,
2135 filename: str,
2136 flags: int,
2137 optimize: int,
2138 peephole_enabled: bool = True,
2139 ast_optimizer_enabled: bool = True,
2140 ):
2141 s = symbols.SymbolVisitor()
2142 walk(tree, s)
2143
2144 graph = cls.flow_graph(
2145 name, filename, s.scopes[tree], peephole_enabled=peephole_enabled
2146 )
2147 code_gen = cls(None, tree, s, graph, flags, optimize)
2148 walk(tree, code_gen)
2149 return code_gen
2150
2151
2152class Python37CodeGenerator(CodeGenerator):
2153 flow_graph = pyassem.PyFlowGraph37
2154
2155 @classmethod
2156 def make_code_gen(
2157 cls,
2158 name: str,
2159 tree: AST,
2160 filename: str,
2161 flags: int,
2162 optimize: int,
2163 peephole_enabled: bool = True,
2164 ast_optimizer_enabled: bool = True,
2165 ):
2166 if ast_optimizer_enabled:
2167 tree = cls.optimize_tree(optimize, tree)
2168 s = symbols.SymbolVisitor()
2169 walk(tree, s)
2170
2171 graph = cls.flow_graph(
2172 name, filename, s.scopes[tree], peephole_enabled=peephole_enabled
2173 )
2174 code_gen = cls(None, tree, s, graph, flags, optimize)
2175 walk(tree, code_gen)
2176 return code_gen
2177
2178 @classmethod
2179 def optimize_tree(self, optimize: int, tree: AST):
2180 return AstOptimizer(optimize=optimize > 0).visit(tree)
2181
2182 def visitCall(self, node):
2183 if (
2184 node.keywords
2185 or not isinstance(node.func, ast.Attribute)
2186 or not isinstance(node.func.ctx, ast.Load)
2187 or any(isinstance(arg, ast.Starred) for arg in node.args)
2188 ):
2189 # We cannot optimize this call
2190 return super().visitCall(node)
2191
2192 self.update_lineno(node)
2193 self.visit(node.func.value)
2194 self.emit("LOAD_METHOD", self.mangle(node.func.attr))
2195 for arg in node.args:
2196 self.visit(arg)
2197 self.emit("CALL_METHOD", len(node.args))
2198
2199 def findFutures(self, node):
2200 consts = self.consts
2201 future_flags = self.flags & consts.PyCF_MASK
2202 for feature in future.find_futures(node):
2203 if feature == "barry_as_FLUFL":
2204 future_flags |= consts.CO_FUTURE_BARRY_AS_BDFL
2205 elif feature == "annotations":
2206 future_flags |= consts.CO_FUTURE_ANNOTATIONS
2207 return future_flags
2208
2209 def _visitAnnotation(self, node):
2210 consts = self.consts
2211 if self.module_gen.future_flags & consts.CO_FUTURE_ANNOTATIONS:
2212 self.emit("LOAD_CONST", to_expr(node))
2213 else:
2214 self.visit(node)
2215
2216 def emitStoreAnnotation(self, name: str, annotation: ast.expr):
2217 assert self.did_setup_annotations
2218
2219 self._visitAnnotation(annotation)
2220 self.emit("LOAD_NAME", "__annotations__")
2221 mangled = self.mangle(name)
2222 self.emit("LOAD_CONST", mangled)
2223 self.emit("STORE_SUBSCR")
2224
2225 def emitImportAs(self, name: str, asname: str):
2226 elts = name.split(".")
2227 if len(elts) == 1:
2228 self.storeName(asname)
2229 return
2230 first = True
2231 for elt in elts[1:]:
2232 if not first:
2233 self.emit("ROT_TWO")
2234 self.emit("POP_TOP")
2235 self.emit("IMPORT_FROM", elt)
2236 first = False
2237 self.storeName(asname)
2238 self.emit("POP_TOP")
2239
2240 def compileJumpIf(self, test, next, is_if_true):
2241 if isinstance(test, ast.UnaryOp):
2242 if isinstance(test.op, ast.Not):
2243 # Compile to remove not operation
2244 self.compileJumpIf(test.operand, next, not is_if_true)
2245 return
2246 elif isinstance(test, ast.BoolOp):
2247 is_or = isinstance(test.op, ast.Or)
2248 skip_jump = next
2249 if is_if_true != is_or:
2250 skip_jump = self.newBlock()
2251
2252 for node in test.values[:-1]:
2253 self.compileJumpIf(node, skip_jump, is_or)
2254
2255 self.compileJumpIf(test.values[-1], next, is_if_true)
2256
2257 if skip_jump is not next:
2258 self.nextBlock(skip_jump)
2259 return
2260 elif isinstance(test, ast.IfExp):
2261 end = self.newBlock("end")
2262 orelse = self.newBlock("orelse")
2263 # Jump directly to orelse if test matches
2264 self.compileJumpIf(test.test, orelse, 0)
2265 # Jump directly to target if test is true and body is matches
2266 self.compileJumpIf(test.body, next, is_if_true)
2267 self.emit("JUMP_FORWARD", end)
2268 # Jump directly to target if test is true and orelse matches
2269 self.nextBlock(orelse)
2270 self.compileJumpIf(test.orelse, next, is_if_true)
2271
2272 self.nextBlock(end)
2273 return
2274 elif isinstance(test, ast.Compare):
2275 if len(test.ops) > 1:
2276 cleanup = self.newBlock()
2277 self.visit(test.left)
2278 for op, comparator in zip(test.ops[:-1], test.comparators[:-1]):
2279 self.emitChainedCompareStep(
2280 op, comparator, cleanup, "POP_JUMP_IF_FALSE"
2281 )
2282 self.visit(test.comparators[-1])
2283 self.emit("COMPARE_OP", self._cmp_opcode[type(test.ops[-1])])
2284 self.emit(
2285 "POP_JUMP_IF_TRUE" if is_if_true else "POP_JUMP_IF_FALSE", next
2286 )
2287 end = self.newBlock()
2288 self.emit("JUMP_FORWARD", end)
2289 self.nextBlock(cleanup)
2290 self.emit("POP_TOP")
2291 if not is_if_true:
2292 self.emit("JUMP_FORWARD", next)
2293 self.nextBlock(end)
2294 return
2295
2296 self.visit(test)
2297 self.emit("POP_JUMP_IF_TRUE" if is_if_true else "POP_JUMP_IF_FALSE", next)
2298 return True
2299
2300 def visitConstant(self, node: ast.Constant):
2301 self.update_lineno(node)
2302 self.emit("LOAD_CONST", node.value)
2303
2304 def emitAsyncIterYieldFrom(self):
2305 pass
2306
2307 def checkAsyncWith(self, node):
2308 if not self.scope.coroutine:
2309 raise SyntaxError(
2310 "'async with' outside async function", self.syntax_error_position(node)
2311 )
2312
2313 def visitAsyncWith(self, node):
2314 self.checkAsyncWith(node)
2315 return super().visitAsyncWith(node)
2316
2317 def emit_try_finally(self, node, try_body, finalbody, except_protect=False):
2318 body = self.newBlock()
2319 final = self.newBlock()
2320 self.emit("SETUP_FINALLY", final)
2321 self.nextBlock(body)
2322 self.setups.push((TRY_FINALLY, body))
2323 try_body()
2324 self.emit("POP_BLOCK")
2325 self.setups.pop()
2326 self.emit("LOAD_CONST", None)
2327 self.nextBlock(final)
2328 self.setups.push((END_FINALLY, final))
2329 finalbody()
2330 self.emit("END_FINALLY")
2331 if except_protect:
2332 self.emit("POP_EXCEPT")
2333 self.setups.pop()
2334
2335 def skip_func_docstring(self, body):
2336 return body
2337
2338 def emitMapUnpack(self, containers, is_unpacking):
2339 if containers > 1 or is_unpacking:
2340 self.emit("BUILD_MAP_UNPACK", containers)
2341
2342 def conjure_arguments(self, args: List[ast.arg]) -> ast.arguments:
2343 return ast.arguments(args, None, [], [], None, [])
2344
2345
2346class Entry:
2347 kind: int
2348 block: pyassem.Block
2349 exit: Optional[pyassem.Block]
2350
2351 def __init__(self, kind, block, exit):
2352 self.kind = kind
2353 self.block = block
2354 self.exit = exit
2355
2356
2357class Python38CodeGenerator(Python37CodeGenerator):
2358 flow_graph = pyassem.PyFlowGraph38
2359 consts = consts38
2360
2361 @classmethod
2362 # pyre-fixme[14]: `optimize_tree` overrides method defined in
2363 # `Python37CodeGenerator` inconsistently.
2364 def optimize_tree(cls, optimize: int, tree: AST):
2365 return AstOptimizer38(optimize=optimize > 0).visit(tree)
2366
2367 def make_function_graph(
2368 self, func, filename: str, scopes, class_name: str, name: str, first_lineno: int
2369 ) -> PyFlowGraph:
2370 args = [
2371 misc.mangle(elt.arg, class_name)
2372 for elt in itertools.chain(func.args.posonlyargs, func.args.args)
2373 ]
2374 kwonlyargs = [misc.mangle(elt.arg, class_name) for elt in func.args.kwonlyargs]
2375
2376 starargs = []
2377 if func.args.vararg:
2378 starargs.append(func.args.vararg.arg)
2379 if func.args.kwarg:
2380 starargs.append(func.args.kwarg.arg)
2381
2382 scope = scopes[func]
2383 graph = self.flow_graph(
2384 name,
2385 filename,
2386 scope,
2387 flags=self.get_graph_flags(func, scope),
2388 args=args,
2389 kwonlyargs=kwonlyargs,
2390 starargs=starargs,
2391 optimized=1,
2392 docstring=self.get_docstring(func),
2393 firstline=first_lineno,
2394 peephole_enabled=self.graph.peephole_enabled,
2395 posonlyargs=len(func.args.posonlyargs),
2396 )
2397
2398 return graph
2399
2400 def visitWhile(self, node):
2401 self.set_lineno(node)
2402
2403 test_const = self.get_bool_const(node.test)
2404 loop = self.newBlock("while_loop")
2405 else_ = self.newBlock("while_else")
2406 after = self.newBlock("while_after")
2407
2408 self.push_loop(WHILE_LOOP, loop, after)
2409
2410 if test_const is False:
2411 with self.noEmit():
2412 self.visit(node.test)
2413 self.visit(node.body)
2414 self.pop_loop()
2415 if node.orelse:
2416 self.visit(node.orelse)
2417 self.nextBlock(after)
2418 return
2419 elif test_const is True:
2420 # emulate co_firstlineno behavior of C compiler
2421 self.graph.maybeEmitSetLineno()
2422
2423 self.nextBlock(loop)
2424
2425 with self.maybeEmit(test_const is not True):
2426 self.compileJumpIf(node.test, else_ or after, False)
2427
2428 self.nextBlock(label="while_body")
2429 self.visit(node.body)
2430 self.emit("JUMP_ABSOLUTE", loop)
2431
2432 with self.maybeEmit(test_const is not True):
2433 self.nextBlock(else_ or after) # or just the POPs if not else clause
2434
2435 self.pop_loop()
2436 if node.orelse:
2437 self.visit(node.orelse)
2438 self.nextBlock(after)
2439
2440 def visitNamedExpr(self, node: ast.NamedExpr):
2441 self.update_lineno(node)
2442 self.visit(node.value)
2443 self.emit("DUP_TOP")
2444 self.visit(node.target)
2445
2446 def push_loop(self, kind, start, end):
2447 self.setups.push(Entry(kind, start, end))
2448
2449 def pop_loop(self):
2450 self.setups.pop()
2451
2452 def visitAsyncFor(self, node):
2453 start = self.newBlock("async_for_try")
2454 except_ = self.newBlock("except")
2455 end = self.newBlock("end")
2456
2457 self.set_lineno(node)
2458 self.visit(node.iter)
2459 self.emit("GET_AITER")
2460
2461 self.nextBlock(start)
2462
2463 self.setups.push(Entry(FOR_LOOP, start, end))
2464 self.emit("SETUP_FINALLY", except_)
2465 self.nextBlock()
2466 self.emit("GET_ANEXT")
2467 self.emit("LOAD_CONST", None)
2468 self.emit("YIELD_FROM")
2469 self.emit("POP_BLOCK")
2470 self.visit(node.target)
2471 self.visit(node.body)
2472 self.emit("JUMP_ABSOLUTE", start)
2473 self.setups.pop()
2474
2475 self.nextBlock(except_)
2476 self.emit("END_ASYNC_FOR")
2477 if node.orelse:
2478 self.visit(node.orelse)
2479 self.nextBlock(end)
2480
2481 def unwind_setup_entry(self, e: Entry, preserve_tos: int) -> None:
2482 if e.kind == WHILE_LOOP:
2483 return
2484 if e.kind == END_FINALLY:
2485 e.exit = None
2486 self.emit("POP_FINALLY", preserve_tos)
2487 if preserve_tos:
2488 self.emit("ROT_TWO")
2489 self.emit("POP_TOP")
2490 elif e.kind == FOR_LOOP:
2491 if preserve_tos:
2492 self.emit("ROT_TWO")
2493 self.emit("POP_TOP")
2494 elif e.kind == EXCEPT:
2495 self.emit("POP_BLOCK")
2496 elif e.kind == TRY_FINALLY:
2497 self.emit("POP_BLOCK")
2498 self.emit("CALL_FINALLY", e.exit)
2499 elif e.kind == TRY_FINALLY_BREAK:
2500 self.emit("POP_BLOCK")
2501 if preserve_tos:
2502 self.emit("ROT_TWO")
2503 self.emit("POP_TOP")
2504 self.emit("CALL_FINALLY", e.exit)
2505 else:
2506 self.emit("CALL_FINALLY", e.exit)
2507 self.emit("POP_TOP")
2508 elif e.kind in (WITH, ASYNC_WITH):
2509 self.emit("POP_BLOCK")
2510 if preserve_tos:
2511 self.emit("ROT_TWO")
2512 self.emit("BEGIN_FINALLY")
2513 self.emit("WITH_CLEANUP_START")
2514 if e.kind == ASYNC_WITH:
2515 self.emit("GET_AWAITABLE")
2516 self.emit("LOAD_CONST", None)
2517 self.emit("YIELD_FROM")
2518 self.emit("WITH_CLEANUP_FINISH")
2519 self.emit("POP_FINALLY", 0)
2520 elif e.kind == HANDLER_CLEANUP:
2521 if preserve_tos:
2522 self.emit("ROT_FOUR")
2523 if e.exit:
2524 self.emit("POP_BLOCK")
2525 self.emit("POP_EXCEPT")
2526 self.emit("CALL_FINALLY", e.exit)
2527 else:
2528 self.emit("POP_EXCEPT")
2529 else:
2530 raise Exception(f"Unexpected kind {e.kind}")
2531
2532 def visitContinue(self, node):
2533 self.set_lineno(node)
2534
2535 for e in reversed(self.setups):
2536 if e.kind in (FOR_LOOP, WHILE_LOOP):
2537 self.emit("JUMP_ABSOLUTE", e.block)
2538 return
2539 self.unwind_setup_entry(e, 0)
2540 raise SyntaxError(
2541 "'continue' not properly in loop", self.syntax_error_position(node)
2542 )
2543
2544 def unwind_setup_entries(self, preserve_tos: bool) -> None:
2545 for e in reversed(self.setups):
2546 self.unwind_setup_entry(e, preserve_tos)
2547
2548 def visitReturn(self, node):
2549 self.checkReturn(node)
2550
2551 self.set_lineno(node)
2552
2553 preserve_tos = bool(node.value and not isinstance(node.value, ast.Constant))
2554 if preserve_tos:
2555 self.visit(node.value)
2556 self.unwind_setup_entries(preserve_tos)
2557 if not node.value:
2558 self.emit("LOAD_CONST", None)
2559 elif not preserve_tos:
2560 self.visit(node.value)
2561
2562 self.emit("RETURN_VALUE")
2563
2564 def compile_async_comprehension(self, generators, gen_index, elt, val, type, load_iter_from_dot0):
2565 start = self.newBlock("start")
2566 except_ = self.newBlock("except")
2567 if_cleanup = self.newBlock("if_cleanup")
2568
2569 gen = generators[gen_index]
2570 if load_iter_from_dot0:
2571 self.loadName(".0")
2572 else:
2573 self.visit(gen.iter)
2574 self.emit("GET_AITER")
2575 self.emitAsyncIterYieldFrom()
2576
2577 self.nextBlock(start)
2578 self.emit("SETUP_FINALLY", except_)
2579 self.nextBlock()
2580 self.emit("GET_ANEXT")
2581 self.emit("LOAD_CONST", None)
2582 self.emit("YIELD_FROM")
2583 self.emit("POP_BLOCK")
2584 self.visit(gen.target)
2585
2586 for if_ in gen.ifs:
2587 self.compileJumpIf(if_, if_cleanup, False)
2588 self.newBlock()
2589
2590 gen_index += 1
2591 if gen_index < len(generators):
2592 self.compile_comprehension_body(generators, gen_index, elt, val, type)
2593 elif type is ast.GeneratorExp:
2594 self.visit(elt)
2595 self.emit("YIELD_VALUE")
2596 self.emit("POP_TOP")
2597 elif type is ast.ListComp:
2598 self.visit(elt)
2599 self.emit("LIST_APPEND", gen_index + 1)
2600 elif type is ast.SetComp:
2601 self.visit(elt)
2602 self.emit("SET_ADD", gen_index + 1)
2603 elif type is ast.DictComp:
2604 self.compile_dictcomp_element(elt, val)
2605 self.emit("MAP_ADD", gen_index + 1)
2606 else:
2607 raise NotImplementedError("unknown comprehension type")
2608
2609 self.nextBlock(if_cleanup)
2610 self.emit("JUMP_ABSOLUTE", start)
2611
2612 self.nextBlock(except_)
2613 self.emit("END_ASYNC_FOR")
2614
2615 def compile_dictcomp_element(self, elt, val):
2616 # For Py38+, the order of evaluation was reversed.
2617 self.visit(elt)
2618 self.visit(val)
2619
2620 def visitTryExcept(self, node):
2621 body = self.newBlock("try_body")
2622 except_ = self.newBlock("try_handlers")
2623 orElse = self.newBlock("try_else")
2624 end = self.newBlock("try_end")
2625
2626 self.emit("SETUP_FINALLY", except_)
2627 self.nextBlock(body)
2628
2629 self.setups.push(Entry(EXCEPT, body, None))
2630 self.visit(node.body)
2631 self.emit("POP_BLOCK")
2632 self.setups.pop()
2633
2634 self.emit("JUMP_FORWARD", orElse)
2635 self.nextBlock(except_)
2636
2637 last = len(node.handlers) - 1
2638 for i in range(len(node.handlers)):
2639 handler = node.handlers[i]
2640 expr = handler.type
2641 target = handler.name
2642 body = handler.body
2643 self.set_lineno(handler)
2644 except_ = self.newBlock(f"try_except_{i}")
2645 if expr:
2646 self.emit("DUP_TOP")
2647 self.visit(expr)
2648 self.emit("COMPARE_OP", "exception match")
2649 self.emit("POP_JUMP_IF_FALSE", except_)
2650 elif i < last:
2651 raise SyntaxError(
2652 "default 'except:' must be last",
2653 self.syntax_error_position(handler),
2654 )
2655 else:
2656 self.set_lineno(handler)
2657 self.emit("POP_TOP")
2658 if target:
2659 cleanup_end = self.newBlock(f"try_cleanup_end{i}")
2660 cleanup_body = self.newBlock(f"try_cleanup_body{i}")
2661 self.storeName(target)
2662 self.emit("POP_TOP")
2663 self.emit("SETUP_FINALLY", cleanup_end)
2664 self.nextBlock(cleanup_body)
2665 self.setups.push(Entry(HANDLER_CLEANUP, cleanup_body, cleanup_end))
2666 self.visit(body)
2667 self.emit("POP_BLOCK")
2668 self.emit("BEGIN_FINALLY")
2669 self.setups.pop()
2670
2671 self.nextBlock(cleanup_end)
2672 self.setups.push(Entry(END_FINALLY, cleanup_end, None))
2673 self.emit("LOAD_CONST", None)
2674 self.storeName(target)
2675 self.delName(target)
2676 self.emit("END_FINALLY")
2677 self.emit("POP_EXCEPT")
2678 self.setups.pop()
2679 else:
2680 cleanup_body = self.newBlock(f"try_cleanup_body{i}")
2681 self.emit("POP_TOP")
2682 self.emit("POP_TOP")
2683 self.nextBlock(cleanup_body)
2684 self.setups.push(Entry(HANDLER_CLEANUP, cleanup_body, None))
2685 self.visit(body)
2686 self.emit("POP_EXCEPT")
2687 self.setups.pop()
2688 self.emit("JUMP_FORWARD", end)
2689 self.nextBlock(except_)
2690
2691 self.emit("END_FINALLY")
2692 self.nextBlock(orElse)
2693 self.visit(node.orelse)
2694 self.nextBlock(end)
2695
2696 def visitBreak(self, node):
2697 self.set_lineno(node)
2698 for b in reversed(self.setups):
2699 self.unwind_setup_entry(b, 0)
2700 if b.kind == WHILE_LOOP or b.kind == FOR_LOOP:
2701 self.emit("JUMP_ABSOLUTE", b.exit)
2702 return
2703 raise SyntaxError("'break' outside loop", self.syntax_error_position(node))
2704
2705 def emit_try_finally(self, node, try_body, finalbody, except_protect=False):
2706 body = self.newBlock("try_finally_body")
2707 end = self.newBlock("try_finally_end")
2708
2709 self.set_lineno(node)
2710 break_finally = True
2711
2712 # compile FINALLY_END out of order to match CPython
2713 with self.graph.new_compile_scope() as compile_end_finally:
2714 self.nextBlock(end)
2715
2716 self.setups.push(Entry(END_FINALLY, end, end))
2717 finalbody()
2718 self.emit("END_FINALLY")
2719 break_finally = self.setups[-1].exit is None
2720 if break_finally:
2721 self.emit("POP_TOP")
2722 self.setups.pop()
2723
2724 self.set_lineno(node)
2725 if break_finally:
2726 self.emit("LOAD_CONST", None)
2727 self.emit("SETUP_FINALLY", end)
2728 self.nextBlock(body)
2729 self.setups.push(
2730 Entry(TRY_FINALLY_BREAK if break_finally else TRY_FINALLY, body, end)
2731 )
2732 try_body()
2733 self.emit("POP_BLOCK")
2734 self.emit("BEGIN_FINALLY")
2735 self.setups.pop()
2736
2737 self.graph.apply_from_scope(compile_end_finally)
2738
2739 def visitWith_(self, node, kind, pos=0):
2740 item = node.items[pos]
2741
2742 block = self.newBlock("with_block")
2743 finally_ = self.newBlock("with_finally")
2744 self.visit(item.context_expr)
2745 if kind == ASYNC_WITH:
2746 self.emit("BEFORE_ASYNC_WITH")
2747 self.emit("GET_AWAITABLE")
2748 self.emit("LOAD_CONST", None)
2749 self.emit("YIELD_FROM")
2750 self.emit("SETUP_ASYNC_WITH", finally_)
2751 else:
2752 self.emit("SETUP_WITH", finally_)
2753
2754 self.nextBlock(block)
2755 self.setups.push(Entry(kind, block, finally_))
2756 if item.optional_vars:
2757 self.visit(item.optional_vars)
2758 else:
2759 self.emit("POP_TOP")
2760
2761 if pos + 1 < len(node.items):
2762 self.visitWith_(node, kind, pos + 1)
2763 else:
2764 self.visit(node.body)
2765
2766 self.emit("POP_BLOCK")
2767 self.emit("BEGIN_FINALLY")
2768 self.setups.pop()
2769
2770 self.nextBlock(finally_)
2771
2772 self.setups.push(Entry(END_FINALLY, finally_, None))
2773 self.emit("WITH_CLEANUP_START")
2774 if kind == ASYNC_WITH:
2775 self.emit("GET_AWAITABLE")
2776 self.emit("LOAD_CONST", None)
2777 self.emit("YIELD_FROM")
2778
2779 self.emit("WITH_CLEANUP_FINISH")
2780 self.emit("END_FINALLY")
2781 self.setups.pop()
2782
2783 def visitWith(self, node):
2784 self.set_lineno(node)
2785 self.visitWith_(node, WITH, 0)
2786
2787 def visitAsyncWith(self, node, pos=0):
2788 self.checkAsyncWith(node)
2789 self.visitWith_(node, ASYNC_WITH, 0)
2790
2791 def annotate_args(self, args: ast.arguments) -> List[str]:
2792 ann_args = []
2793 for arg in args.args:
2794 self.annotate_arg(arg, ann_args)
2795 for arg in args.posonlyargs:
2796 self.annotate_arg(arg, ann_args)
2797 if args.vararg:
2798 # pyre-fixme[6]: Expected `arg` for 1st param but got `Optional[_ast.arg]`.
2799 self.annotate_arg(args.vararg, ann_args)
2800 for arg in args.kwonlyargs:
2801 self.annotate_arg(arg, ann_args)
2802 if args.kwarg:
2803 # pyre-fixme[6]: Expected `arg` for 1st param but got `Optional[_ast.arg]`.
2804 self.annotate_arg(args.kwarg, ann_args)
2805 return ann_args
2806
2807 def conjure_arguments(self, args: List[ast.arg]) -> ast.arguments:
2808 return ast.arguments([], args, None, [], [], None, [])
2809
2810 def skip_visit(self):
2811 """On Py38 we never want to skip visiting nodes."""
2812 return False
2813
2814 def visit(self, node: Union[Sequence[AST], AST], *args):
2815 # Note down the old line number
2816 could_be_multiline_expr = isinstance(node, ast.expr)
2817 old_lineno = self.graph.lineno if could_be_multiline_expr else None
2818
2819 ret = super().visit(node, *args)
2820
2821 if old_lineno is not None and old_lineno != self.graph.lineno:
2822 self.graph.lineno = old_lineno
2823 self.graph.lineno_set = False
2824
2825 return ret
2826
2827
2828class CinderCodeGenerator(Python38CodeGenerator):
2829 flow_graph = pyassem.PyFlowGraphCinder
2830
2831 def set_qual_name(self, qualname):
2832 self._qual_name = qualname
2833
2834 def getCode(self):
2835 code = super().getCode()
2836 # pyre-fixme [21]: cinder
2837 from cinder import _set_qualname
2838
2839 _set_qualname(code, self._qual_name)
2840 return code
2841
2842 def _nameOp(self, prefix, name) -> None:
2843 if (
2844 prefix == "LOAD"
2845 and name == "super"
2846 and isinstance(self.scope, symbols.FunctionScope)
2847 ):
2848 scope = self.scope.check_name(name)
2849 if scope in (SC_GLOBAL_EXPLICIT, SC_GLOBAL_IMPLICIT):
2850 self.scope.suppress_jit = True
2851 super()._nameOp(prefix, name)
2852
2853 def _is_super_call(self, node):
2854 if (
2855 not isinstance(node, ast.Call)
2856 or not isinstance(node.func, ast.Name)
2857 or node.func.id != "super"
2858 or node.keywords
2859 ):
2860 return False
2861
2862 # check that 'super' only appear as implicit global:
2863 # it is not defined in local or modules scope
2864 if (
2865 self.scope.check_name("super") != SC_GLOBAL_IMPLICIT
2866 or self.module_gen.scope.check_name("super") != SC_GLOBAL_IMPLICIT
2867 ):
2868 return False
2869
2870 if len(node.args) == 2:
2871 return True
2872 if len(node.args) == 0:
2873 if len(self.scope.params) == 0:
2874 return False
2875 return self.scope.check_name("__class__") == SC_FREE
2876
2877 return False
2878
2879 def _emit_args_for_super(self, super_call, attr):
2880 if len(super_call.args) == 0:
2881 self.loadName("__class__")
2882 self.loadName(next(iter(self.scope.params)))
2883 else:
2884 for arg in super_call.args:
2885 self.visit(arg)
2886 return (self.mangle(attr), len(super_call.args) == 0)
2887
2888 def visitAttribute(self, node):
2889 if isinstance(node.ctx, ast.Load) and self._is_super_call(node.value):
2890 self.emit("LOAD_GLOBAL", "super")
2891 load_arg = self._emit_args_for_super(node.value, node.attr)
2892 self.emit("LOAD_ATTR_SUPER", load_arg)
2893 else:
2894 super().visitAttribute(node)
2895
2896 def visitCall(self, node):
2897 if (
2898 not isinstance(node.func, ast.Attribute)
2899 or not isinstance(node.func.ctx, ast.Load)
2900 or node.keywords
2901 or any(isinstance(arg, ast.Starred) for arg in node.args)
2902 ):
2903 # We cannot optimize this call
2904 return super().visitCall(node)
2905
2906 self.update_lineno(node)
2907 if self._is_super_call(node.func.value):
2908 self.emit("LOAD_GLOBAL", "super")
2909 load_arg = self._emit_args_for_super(node.func.value, node.func.attr)
2910 self.emit("LOAD_METHOD_SUPER", load_arg)
2911 for arg in node.args:
2912 self.visit(arg)
2913 self.emit("CALL_METHOD", len(node.args))
2914 return
2915
2916 self.visit(node.func.value)
2917 self.emit("LOAD_METHOD", self.mangle(node.func.attr))
2918 for arg in node.args:
2919 self.visit(arg)
2920
2921 self.emit("CALL_METHOD", len(node.args))
2922
2923
2924def get_default_generator():
2925
2926 if "cinder" in sys.version:
2927 return CinderCodeGenerator
2928 if sys.version_info >= (3, 8):
2929 return Python38CodeGenerator
2930 if sys.version_info >= (3, 7):
2931 return Python37CodeGenerator
2932
2933 return CodeGenerator
2934
2935
2936def get_docstring(node):
2937 if (
2938 node.body
2939 and isinstance(node.body[0], ast.Expr)
2940 and isinstance(node.body[0].value, ast.Str)
2941 ):
2942 return node.body[0].value.s
2943
2944
2945def findOp(node):
2946 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
2947 v = OpFinder()
2948 v.VERBOSE = 0
2949 walk(node, v)
2950 return v.op
2951
2952
2953class OpFinder:
2954 def __init__(self):
2955 self.op = None
2956
2957 def visitAssName(self, node):
2958 if self.op is None:
2959 self.op = node.flags
2960 elif self.op != node.flags:
2961 raise ValueError("mixed ops in stmt")
2962
2963 visitAssAttr = visitAssName
2964 visitSubscript = visitAssName
2965
2966
2967class Delegator:
2968 """Base class to support delegation for augmented assignment nodes
2969
2970 To generator code for augmented assignments, we use the following
2971 wrapper classes. In visitAugAssign, the left-hand expression node
2972 is visited twice. The first time the visit uses the normal method
2973 for that node . The second time the visit uses a different method
2974 that generates the appropriate code to perform the assignment.
2975 These delegator classes wrap the original AST nodes in order to
2976 support the variant visit methods.
2977 """
2978
2979 def __init__(self, obj):
2980 self.obj = obj
2981
2982 def __getattr__(self, attr):
2983 return getattr(self.obj, attr)
2984
2985 def __eq__(self, other):
2986 return other == self.obj
2987
2988 def __hash__(self):
2989 return hash(self.obj)
2990
2991
2992class AugAttribute(Delegator):
2993 pass
2994
2995
2996class AugName(Delegator):
2997 pass
2998
2999
3000class AugSubscript(Delegator):
3001 pass
3002
3003
3004class CompInner(Delegator):
3005 def __init__(self, obj, nested_scope, init_inst, elt_nodes, elt_insts):
3006 Delegator.__init__(self, obj)
3007 self.nested_scope = nested_scope
3008 self.init_inst = init_inst
3009 self.elt_nodes = elt_nodes
3010 self.elt_insts = elt_insts
3011
3012
3013wrapper = {
3014 ast.Attribute: AugAttribute,
3015 ast.Name: AugName,
3016 ast.Subscript: AugSubscript,
3017}
3018
3019
3020def wrap_aug(node):
3021 return wrapper[node.__class__](node)
3022
3023
3024PythonCodeGenerator = get_default_generator()
3025
3026
3027if __name__ == "__main__":
3028 for file in sys.argv[1:]:
3029 compileFile(file)