this repo has no description
at trunk 3029 lines 97 kB view raw
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)