├── InjCode.py ├── LICENSE ├── README.md ├── injector ├── dumper.py ├── executor.dll └── pyinject.exe ├── main.py ├── temp └── 1.png └── unpyc ├── __pycache__ └── unpyc3.cpython-39.pyc ├── setup.py ├── unpyc ├── __pycache__ │ ├── opcodes.cpython-39.pyc │ └── unpyc3.cpython-39.pyc ├── opcodes.py └── unpyc3.py └── unpyc3.py /InjCode.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | import importlib.util 3 | import pathlib 4 | import sys 5 | 6 | print(" [V] INJECTED") 7 | 8 | 9 | try: 10 | 11 | # LOAD LIB FROM LOCAL FILES 12 | 13 | # path = str(pathlib.Path().resolve()) 14 | # dis_path = f"{path}\\unpyc\\unpyc\\unpyc3.py" 15 | # print(dis_path) 16 | # spec = importlib.util.spec_from_file_location("unpyc3", dis_path) 17 | # unpyc3 = importlib.util.module_from_spec(spec) 18 | # sys.modules["unpyc3"] = unpyc3 19 | # spec.loader.exec_module(unpyc3) 20 | 21 | 22 | 23 | print("Here u can add your uwu code") 24 | 25 | except: 26 | traceback.print_exc() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 oxyn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # py-decompiler (GxHook) 2 | ## description 3 | gxHook decompiler is a program that injects code into a python process and decompiles it from memory which makes it easier to decompile obfuscated programs (for example pyarmor). 4 | Decompiler work for pyinstaller only ! 5 | tested python version 3.8 6 | 7 | pyinject source https://github.com/acureau/pynject 8 | #### Join us 9 | https://discord.gg/aBeKqAGmMk 10 | 11 | ## Usage 12 | 1. run main.py 13 | 2. select target python process 14 | 3. wait for disassemble 15 | 4. wait for decompile 16 | 5. open dump dir in your exe app folder (all decompiled functions and classes should be in this dir) 17 | 18 | ## Change log 19 | ```diff 20 | v1.0.1 ⋮ 22/07/2023 21 | + The decompiler has been changed making decompilation better and faster 22 | + Added executor option 23 | 24 | v1.0.0 ⋮ 19/07/2023 25 | + Released 26 | ``` 27 | 28 | 29 | ![Test 1](https://github.com/OxynDev/py-decompiler/blob/729501b74702d6cb3bb795e4bb120d208d1ecc84/temp/1.png) 30 | 31 | -------------------------------------------------------------------------------- /injector/dumper.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | import os 4 | import shutil 5 | import inspect 6 | import sys 7 | import importlib.util 8 | import pathlib 9 | import traceback 10 | import threading 11 | 12 | class UwUdecompiler: 13 | 14 | functions = [] 15 | classes = [] 16 | unpyc3 = None 17 | 18 | def __init__(self) -> None: 19 | print(" [V] Decompiler Injected") 20 | self.all = globals().copy() 21 | self.load_unpyc() 22 | self.create_dirs() 23 | self.decompile() 24 | 25 | 26 | def load_unpyc(self): 27 | try: 28 | path = str(pathlib.Path().resolve()) 29 | dis_path = f"{path}\\unpyc\\unpyc\\unpyc3.py" 30 | spec = importlib.util.spec_from_file_location("unpyc3", dis_path) 31 | self.unpyc3 = importlib.util.module_from_spec(spec) 32 | sys.modules["unpyc3"] = self.unpyc3 33 | spec.loader.exec_module(self.unpyc3) 34 | except: 35 | print("Cant load unpyc3 module") 36 | 37 | 38 | def create_dirs(self): 39 | try: 40 | shutil.rmtree("dump") 41 | except: 42 | pass 43 | 44 | dirs = ('functions','classes') 45 | 46 | if not os.path.exists("dump"): 47 | os.makedirs("dump") 48 | 49 | for i in dirs: 50 | if not os.path.exists("dump/" + i): 51 | os.makedirs("dump/" + i) 52 | 53 | def get_id(self): 54 | return ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(3)) 55 | 56 | def get_functions(self): 57 | 58 | 59 | is_function_member = lambda member: inspect.isfunction(member) and member.__module__ == __name__ 60 | functionmember = inspect.getmembers(sys.modules[__name__], is_function_member) 61 | 62 | for i in functionmember: 63 | self.functions.append(i[0]) 64 | 65 | def get_classes(self): 66 | 67 | is_class_member = lambda member: inspect.isclass(member) and member.__module__ == __name__ 68 | clsmembers = inspect.getmembers(sys.modules[__name__], is_class_member) 69 | 70 | for i in clsmembers: 71 | self.classes.append(i[0]) 72 | 73 | 74 | 75 | 76 | def get_code(self, data, dir): 77 | print("X") 78 | try: 79 | 80 | if dir == 'functions': 81 | code = self.unpyc3.decompile(self.all[data]) 82 | print(code) 83 | with open("dump/" + dir + "/" + data + "_" + self.get_id() + ".py", 'w') as file: 84 | file.write(str(code)) 85 | file.close() 86 | 87 | elif dir == 'classes': 88 | if data != "UwUdecompiler": 89 | class_methods = inspect.getmembers(self.all[data], predicate=inspect.isfunction) 90 | for method_name, method in class_methods: 91 | code = self.unpyc3.decompile(method) 92 | print(code) 93 | if not os.path.exists("dump/classes/"+data): 94 | os.makedirs("dump/classes/"+data) 95 | 96 | with open("dump/classes/"+data + "/" + method_name + "_" + self.get_id() + ".py", 'w') as file: 97 | file.write(str(code)) 98 | file.close() 99 | 100 | except: 101 | traceback.print_exc() 102 | 103 | def save_functions(self): 104 | self.get_functions() 105 | print(self.functions) 106 | for i in self.functions: 107 | threading.Thread(target=self.get_code, args=(i, 'functions',)).start() 108 | 109 | def save_classes(self): 110 | self.get_classes() 111 | print(self.classes) 112 | for i in self.classes: 113 | threading.Thread(target=self.get_code, args=(i, 'classes',)).start() 114 | 115 | def decompile(self): 116 | self.save_functions() 117 | self.save_classes() 118 | 119 | print("[!] DONE") 120 | 121 | UwUdecompiler() 122 | -------------------------------------------------------------------------------- /injector/executor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/injector/executor.dll -------------------------------------------------------------------------------- /injector/pyinject.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/injector/pyinject.exe -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import subprocess, re, os, time, shutil, httpx 2 | from win32com.client import GetObject 3 | 4 | class Decompiler: 5 | def __init__(self): 6 | self.start() 7 | 8 | def get_process_list(self): 9 | 10 | res = subprocess.check_output(["injector/pyinject.exe", "scan"]) 11 | pid_base_regex = re.compile(r'Base: (\S+) \| Version: \S+ \| PID: (\d+)') 12 | matches = pid_base_regex.findall(res.decode('utf-8')) 13 | result_list = [{'base': match[0], 'pid': match[1]} for match in matches] 14 | 15 | if len(result_list) > 0: 16 | return result_list 17 | else: 18 | return False 19 | 20 | def inject_disassembler_code(self, pid: int): 21 | subprocess.call(f"injector/pyinject.exe {str(pid)} injector/dumper.py") 22 | 23 | def inject_code(self, pid: int): 24 | subprocess.call(f"injector/pyinject.exe {str(pid)} InjCode.py") 25 | 26 | def process_path(self, process_name): 27 | WMI = GetObject('winmgmts:') 28 | processes = WMI.InstancesOf('Win32_Process') 29 | for p in processes : 30 | if p.Properties_("Name").Value == process_name : 31 | return p.Properties_[7].Value 32 | return False 33 | 34 | def start(self): 35 | os.system("cls") 36 | try: 37 | discord = httpx.get("https://pastebin.com/raw/jSkxLZa3").text 38 | except: 39 | discord == "https://discord.gg/8W6BweksGY" 40 | os.system("cls") 41 | 42 | print("\n",discord, """\n\n ____ _ _ _ 43 | / ___|_ _| | | | ___ ___ | | __ 44 | | | _\ \/ / |_| |/ _ \ / _ \| |/ / 45 | | |_| |> <| _ | (_) | (_) | < 46 | \____/_/\_\_| |_|\___/ \___/|_|\_\\ 47 | """ ,"\n") 48 | 49 | process_list = self.get_process_list() 50 | 51 | if process_list == False: 52 | print("Python process not found") 53 | 54 | else: 55 | print(" Select target process:","\n") 56 | 57 | for i in range(len(process_list)): 58 | print(f" [{i}] " + process_list[i]['base']) 59 | 60 | print("\n") 61 | process_res = input(" [>] : ") 62 | 63 | try: 64 | int(process_res) 65 | except ValueError: 66 | os.system("cls") 67 | print("\n"," [X] Invalid selection") 68 | time.sleep(2) 69 | self.start() 70 | 71 | if (int(process_res) > len(process_list)) or (int(process_res) < 0): 72 | os.system("cls") 73 | print("\n"," [X] Invalid selection") 74 | time.sleep(2) 75 | self.start() 76 | 77 | os.system("cls") 78 | print("\n") 79 | print(" [1] decompiler \n [2] executor","\n\n") 80 | res = input(" [>] : ") 81 | 82 | try: 83 | int(res) 84 | except ValueError: 85 | os.system("cls") 86 | print("\n"," [X] Invalid selection") 87 | time.sleep(2) 88 | self.start() 89 | 90 | if int(res) == 1: 91 | path = self.process_path(process_list[int(process_res)]['base']).split(process_list[int(process_res)]['base'])[0] 92 | try: 93 | shutil.copytree("unpyc", path + "\\unpyc") 94 | except: 95 | pass 96 | 97 | self.inject_disassembler_code(int(process_list[int(process_res)]['pid'])) 98 | 99 | os.system("cls") 100 | print() 101 | input(" [V] Decompiler injected") 102 | time.sleep(3) 103 | 104 | elif int(res) == 2: 105 | os.system("cls") 106 | while True: 107 | self.inject_code(int(process_list[int(process_res)]['pid'])) 108 | print(" Injected") 109 | input(" Press eneter to inject code.py again") 110 | os.system("cls") 111 | else: 112 | os.system("cls") 113 | print("\n"," [X] Invalid selection") 114 | time.sleep(2) 115 | self.start() 116 | 117 | Decompiler() 118 | -------------------------------------------------------------------------------- /temp/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/temp/1.png -------------------------------------------------------------------------------- /unpyc/__pycache__/unpyc3.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/unpyc/__pycache__/unpyc3.cpython-39.pyc -------------------------------------------------------------------------------- /unpyc/setup.py: -------------------------------------------------------------------------------- 1 | #!/system/bin/python3 2 | 3 | from distutils.core import setup 4 | 5 | 6 | try: 7 | from distutils.command.build_py import build_py_2to3 as build_py 8 | except ImportError: 9 | from distutils.command.build_py import build_py 10 | 11 | try: 12 | from distutils.command.build_scripts import ( 13 | build_scripts_2to3 as build_scripts, 14 | ) 15 | except ImportError: 16 | from distutils.command.build_scripts import build_scripts 17 | 18 | setup( 19 | name="unpyc37", 20 | version="0.0.00001", 21 | description="The unpyc37 python package", 22 | author="Unknown", 23 | license="public domain", 24 | packages=[], 25 | scripts=['unpyc3.py'], 26 | cmdclass={'build_py': build_py, 'build_scripts': build_scripts}, 27 | ) 28 | -------------------------------------------------------------------------------- /unpyc/unpyc/__pycache__/opcodes.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/unpyc/unpyc/__pycache__/opcodes.cpython-39.pyc -------------------------------------------------------------------------------- /unpyc/unpyc/__pycache__/unpyc3.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OxynDev/py-decompiler/8c64c3224dd81601f1aff03e4f31a7fae654f4b0/unpyc/unpyc/__pycache__/unpyc3.cpython-39.pyc -------------------------------------------------------------------------------- /unpyc/unpyc/opcodes.py: -------------------------------------------------------------------------------- 1 | """Opcode definitions for both Python 2 and Python 3.""" 2 | 3 | # Because pytype takes too long: 4 | # pytype: skip-file 5 | 6 | 7 | # We define all-uppercase classes, to match their opcode names: 8 | # pylint: disable=invalid-name 9 | 10 | HAS_CONST = 1 # references the constant table 11 | HAS_NAME = 2 # references the name table 12 | HAS_JREL = 4 # relative jump 13 | HAS_JABS = 8 # absolute jump 14 | HAS_JUNKNOWN = 16 # jumps to unknown location 15 | HAS_LOCAL = 32 # references the varnames table 16 | HAS_FREE = 64 # references "free variable" cells 17 | HAS_NARGS = 128 # stores number of args + kwargs 18 | HAS_ARGUMENT = 256 # all opcodes >= 90 19 | NO_NEXT = 512 # doesn't execute the following opcode 20 | STORE_JUMP = 1024 # only stores a jump, doesn't actually execute it 21 | PUSHES_BLOCK = ( 22 | 2048 # starts a block (while, try, finally, with, etc.) 23 | ) 24 | POPS_BLOCK = 4096 # ends a block 25 | 26 | 27 | class Opcode(object): 28 | """An opcode without arguments.""" 29 | 30 | __slots__ = ( 31 | "line", 32 | "index", 33 | "prev", 34 | "next", 35 | "target", 36 | "block_target", 37 | "code", 38 | "type_comment", 39 | ) 40 | FLAGS = 0 41 | 42 | def __init__(self, index, line): 43 | self.index = index 44 | self.line = line 45 | self.target = None 46 | self.code = ( 47 | None # If we have a CodeType or OrderedCode parent 48 | ) 49 | self.type_comment = None 50 | 51 | def at_line(self, line): 52 | """Return a new opcode simliar to this one but with a different line.""" 53 | # Ignore the optional slots (prev, next, block_target). 54 | op = Opcode(self.index, line) 55 | op.target = self.target 56 | op.code = self.code 57 | return op 58 | 59 | def basic_str(self): 60 | """Helper function for the various __str__ formats.""" 61 | return "%d: %d: %s" % ( 62 | self.line, 63 | self.index, 64 | self.__class__.__name__, 65 | ) 66 | 67 | def __str__(self): 68 | if self.type_comment: 69 | return "%s # type: %s" % ( 70 | self.basic_str(), 71 | self.type_comment, 72 | ) 73 | else: 74 | return self.basic_str() 75 | 76 | def __repr__(self): 77 | return self.__class__.__name__ 78 | 79 | @property 80 | def name(self): 81 | return self.__class__.__name__ 82 | 83 | @classmethod 84 | def has_const(cls): 85 | return bool(cls.FLAGS & HAS_CONST) 86 | 87 | @classmethod 88 | def has_name(cls): 89 | return bool(cls.FLAGS & HAS_NAME) 90 | 91 | @classmethod 92 | def has_jrel(cls): 93 | return bool(cls.FLAGS & HAS_JREL) 94 | 95 | @classmethod 96 | def has_jabs(cls): 97 | return bool(cls.FLAGS & HAS_JABS) 98 | 99 | @classmethod 100 | def has_junknown(cls): 101 | return bool(cls.FLAGS & HAS_JUNKNOWN) 102 | 103 | @classmethod 104 | def has_jump(cls): 105 | return bool(cls.FLAGS & (HAS_JREL | HAS_JABS | HAS_JUNKNOWN)) 106 | 107 | @classmethod 108 | def has_local(cls): 109 | return bool(cls.FLAGS & HAS_LOCAL) 110 | 111 | @classmethod 112 | def has_free(cls): 113 | return bool(cls.FLAGS & HAS_FREE) 114 | 115 | @classmethod 116 | def has_nargs(cls): 117 | return bool(cls.FLAGS & HAS_NARGS) 118 | 119 | @classmethod 120 | def has_argument(cls): 121 | return bool(cls.FLAGS & HAS_ARGUMENT) 122 | 123 | @classmethod 124 | def no_next(cls): 125 | return bool(cls.FLAGS & NO_NEXT) 126 | 127 | @classmethod 128 | def carry_on_to_next(cls): 129 | return not cls.FLAGS & NO_NEXT 130 | 131 | @classmethod 132 | def store_jump(cls): 133 | return bool(cls.FLAGS & STORE_JUMP) 134 | 135 | @classmethod 136 | def does_jump(cls): 137 | return cls.has_jump() and not cls.store_jump() 138 | 139 | @classmethod 140 | def pushes_block(cls): 141 | return bool(cls.FLAGS & PUSHES_BLOCK) 142 | 143 | @classmethod 144 | def pops_block(cls): 145 | return bool(cls.FLAGS & POPS_BLOCK) 146 | 147 | @classmethod 148 | def has_arg(cls): 149 | return False 150 | 151 | 152 | class OpcodeWithArg(Opcode): 153 | """An opcode with one argument.""" 154 | 155 | __slots__ = ("arg", "pretty_arg") 156 | 157 | def __init__(self, index, line, arg, pretty_arg): 158 | super(OpcodeWithArg, self).__init__(index, line) 159 | self.arg = arg 160 | self.pretty_arg = pretty_arg 161 | 162 | def __str__(self): 163 | out = "%s %s" % (self.basic_str(), self.pretty_arg) 164 | if self.type_comment: 165 | return "%s # type: %s" % (out, self.type_comment) 166 | else: 167 | return out 168 | 169 | @classmethod 170 | def has_arg(cls): 171 | return True 172 | 173 | 174 | class STOP_CODE(Opcode): 175 | __slots__ = () 176 | FLAGS = NO_NEXT 177 | 178 | 179 | class POP_TOP(Opcode): 180 | __slots__ = () 181 | 182 | 183 | class ROT_TWO(Opcode): 184 | __slots__ = () 185 | 186 | 187 | class ROT_THREE(Opcode): 188 | __slots__ = () 189 | 190 | 191 | class DUP_TOP(Opcode): 192 | __slots__ = () 193 | 194 | 195 | class ROT_FOUR(Opcode): 196 | __slots__ = () 197 | 198 | 199 | class DUP_TOP_TWO(Opcode): 200 | __slots__ = () 201 | 202 | 203 | class NOP(Opcode): 204 | __slots__ = () 205 | 206 | 207 | class UNARY_POSITIVE(Opcode): 208 | __slots__ = () 209 | 210 | 211 | class UNARY_NEGATIVE(Opcode): 212 | __slots__ = () 213 | 214 | 215 | class UNARY_NOT(Opcode): 216 | __slots__ = () 217 | 218 | 219 | class UNARY_CONVERT(Opcode): 220 | __slots__ = () 221 | 222 | 223 | class UNARY_INVERT(Opcode): 224 | __slots__ = () 225 | 226 | 227 | class BINARY_MATRIX_MULTIPLY(Opcode): 228 | __slots__ = () 229 | 230 | 231 | class INPLACE_MATRIX_MULTIPLY(Opcode): 232 | __slots__ = () 233 | 234 | 235 | class BINARY_POWER(Opcode): 236 | __slots__ = () 237 | 238 | 239 | class BINARY_MULTIPLY(Opcode): 240 | __slots__ = () 241 | 242 | 243 | class BINARY_DIVIDE(Opcode): 244 | __slots__ = () 245 | 246 | 247 | class BINARY_MODULO(Opcode): 248 | __slots__ = () 249 | 250 | 251 | class BINARY_ADD(Opcode): 252 | __slots__ = () 253 | 254 | 255 | class BINARY_SUBTRACT(Opcode): 256 | __slots__ = () 257 | 258 | 259 | class BINARY_SUBSCR(Opcode): 260 | __slots__ = () 261 | 262 | 263 | class BINARY_FLOOR_DIVIDE(Opcode): 264 | __slots__ = () 265 | 266 | 267 | class BINARY_TRUE_DIVIDE(Opcode): 268 | __slots__ = () 269 | 270 | 271 | class INPLACE_FLOOR_DIVIDE(Opcode): 272 | __slots__ = () 273 | 274 | 275 | class INPLACE_TRUE_DIVIDE(Opcode): 276 | __slots__ = () 277 | 278 | 279 | class SLICE_0(Opcode): 280 | __slots__ = () 281 | 282 | 283 | class SLICE_1(Opcode): 284 | __slots__ = () 285 | 286 | 287 | class SLICE_2(Opcode): 288 | __slots__ = () 289 | 290 | 291 | class SLICE_3(Opcode): 292 | __slots__ = () 293 | 294 | 295 | class STORE_SLICE_0(Opcode): 296 | __slots__ = () 297 | 298 | 299 | class STORE_SLICE_1(Opcode): 300 | __slots__ = () 301 | 302 | 303 | class STORE_SLICE_2(Opcode): 304 | __slots__ = () 305 | 306 | 307 | class STORE_SLICE_3(Opcode): 308 | __slots__ = () 309 | 310 | 311 | class DELETE_SLICE_0(Opcode): 312 | __slots__ = () 313 | 314 | 315 | class DELETE_SLICE_1(Opcode): 316 | __slots__ = () 317 | 318 | 319 | class DELETE_SLICE_2(Opcode): 320 | __slots__ = () 321 | 322 | 323 | class DELETE_SLICE_3(Opcode): 324 | __slots__ = () 325 | 326 | 327 | class GET_AITER(Opcode): 328 | __slots__ = () 329 | 330 | 331 | class GET_ANEXT(Opcode): 332 | __slots__ = () 333 | 334 | 335 | class BEFORE_ASYNC_WITH(Opcode): 336 | __slots__ = () 337 | 338 | 339 | class STORE_MAP(Opcode): 340 | __slots__ = () 341 | 342 | 343 | class INPLACE_ADD(Opcode): 344 | __slots__ = () 345 | 346 | 347 | class INPLACE_SUBTRACT(Opcode): 348 | __slots__ = () 349 | 350 | 351 | class INPLACE_MULTIPLY(Opcode): 352 | __slots__ = () 353 | 354 | 355 | class INPLACE_DIVIDE(Opcode): 356 | __slots__ = () 357 | 358 | 359 | class INPLACE_MODULO(Opcode): 360 | __slots__ = () 361 | 362 | 363 | class STORE_SUBSCR(Opcode): 364 | __slots__ = () 365 | 366 | 367 | class DELETE_SUBSCR(Opcode): 368 | __slots__ = () 369 | 370 | 371 | class BINARY_LSHIFT(Opcode): 372 | __slots__ = () 373 | 374 | 375 | class BINARY_RSHIFT(Opcode): 376 | __slots__ = () 377 | 378 | 379 | class BINARY_AND(Opcode): 380 | __slots__ = () 381 | 382 | 383 | class BINARY_XOR(Opcode): 384 | __slots__ = () 385 | 386 | 387 | class BINARY_OR(Opcode): 388 | __slots__ = () 389 | 390 | 391 | class INPLACE_POWER(Opcode): 392 | __slots__ = () 393 | 394 | 395 | class GET_ITER(Opcode): 396 | __slots__ = () 397 | 398 | 399 | class GET_YIELD_FROM_ITER(Opcode): 400 | __slots__ = () 401 | 402 | 403 | class STORE_LOCALS(Opcode): 404 | __slots__ = () 405 | 406 | 407 | class PRINT_EXPR(Opcode): 408 | __slots__ = () 409 | 410 | 411 | class PRINT_ITEM(Opcode): 412 | __slots__ = () 413 | 414 | 415 | class PRINT_NEWLINE(Opcode): 416 | __slots__ = () 417 | 418 | 419 | class PRINT_ITEM_TO(Opcode): 420 | __slots__ = () 421 | 422 | 423 | class PRINT_NEWLINE_TO(Opcode): 424 | __slots__ = () 425 | 426 | 427 | class LOAD_BUILD_CLASS(Opcode): 428 | __slots__ = () 429 | 430 | 431 | class YIELD_FROM(Opcode): 432 | FLAGS = HAS_JUNKNOWN 433 | __slots__ = () 434 | 435 | 436 | class GET_AWAITABLE(Opcode): 437 | __slots__ = () 438 | 439 | 440 | class INPLACE_LSHIFT(Opcode): 441 | __slots__ = () 442 | 443 | 444 | class INPLACE_RSHIFT(Opcode): 445 | __slots__ = () 446 | 447 | 448 | class INPLACE_AND(Opcode): 449 | __slots__ = () 450 | 451 | 452 | class INPLACE_XOR(Opcode): 453 | __slots__ = () 454 | 455 | 456 | class INPLACE_OR(Opcode): 457 | __slots__ = () 458 | 459 | 460 | class BREAK_LOOP(Opcode): 461 | FLAGS = HAS_JUNKNOWN | NO_NEXT 462 | __slots__ = () 463 | 464 | 465 | class WITH_CLEANUP(Opcode): 466 | # This opcode changes the block stack, but it should never change its depth. 467 | FLAGS = HAS_JUNKNOWN # might call __exit__ 468 | __slots__ = () 469 | 470 | 471 | class WITH_CLEANUP_START(Opcode): 472 | FLAGS = HAS_JUNKNOWN # might call __exit__ 473 | __slots__ = () 474 | 475 | 476 | class WITH_CLEANUP_FINISH(Opcode): 477 | __slots__ = () 478 | 479 | 480 | class LOAD_LOCALS(Opcode): 481 | __slots__ = () 482 | 483 | 484 | class RETURN_VALUE(Opcode): 485 | FLAGS = HAS_JUNKNOWN | NO_NEXT 486 | __slots__ = () 487 | 488 | 489 | class IMPORT_STAR(Opcode): 490 | __slots__ = () 491 | 492 | 493 | class SETUP_ANNOTATIONS(Opcode): 494 | __slots__ = () 495 | 496 | 497 | class EXEC_STMT(Opcode): 498 | FLAGS = HAS_JUNKNOWN 499 | __slots__ = () 500 | 501 | 502 | class YIELD_VALUE(Opcode): 503 | FLAGS = HAS_JUNKNOWN 504 | __slots__ = () 505 | 506 | 507 | class POP_BLOCK(Opcode): 508 | FLAGS = POPS_BLOCK 509 | __slots__ = () 510 | 511 | 512 | class END_FINALLY(Opcode): 513 | FLAGS = HAS_JUNKNOWN # might re-raise an exception 514 | __slots__ = () 515 | 516 | 517 | class BUILD_CLASS(Opcode): 518 | __slots__ = () 519 | 520 | 521 | class POP_EXCEPT(Opcode): 522 | __slots__ = () 523 | 524 | 525 | class STORE_NAME(OpcodeWithArg): # Indexes into name list 526 | FLAGS = HAS_NAME | HAS_ARGUMENT 527 | __slots__ = () 528 | 529 | 530 | class DELETE_NAME(OpcodeWithArg): # Indexes into name list 531 | FLAGS = HAS_NAME | HAS_ARGUMENT 532 | __slots__ = () 533 | 534 | 535 | class UNPACK_SEQUENCE(OpcodeWithArg): # Arg: Number of tuple items 536 | FLAGS = HAS_ARGUMENT 537 | __slots__ = () 538 | 539 | 540 | class FOR_ITER(OpcodeWithArg): 541 | FLAGS = HAS_JREL | HAS_ARGUMENT 542 | __slots__ = () 543 | 544 | 545 | class LIST_APPEND(OpcodeWithArg): 546 | FLAGS = HAS_ARGUMENT 547 | __slots__ = () 548 | 549 | 550 | class UNPACK_EX(OpcodeWithArg): 551 | FLAGS = HAS_ARGUMENT 552 | __slots__ = () 553 | 554 | 555 | class STORE_ATTR(OpcodeWithArg): # Indexes into name list 556 | FLAGS = HAS_NAME | HAS_ARGUMENT 557 | __slots__ = () 558 | 559 | 560 | class DELETE_ATTR(OpcodeWithArg): # Indexes into name list 561 | FLAGS = HAS_NAME | HAS_ARGUMENT 562 | __slots__ = () 563 | 564 | 565 | class STORE_GLOBAL(OpcodeWithArg): # Indexes into name list 566 | FLAGS = HAS_NAME | HAS_ARGUMENT 567 | __slots__ = () 568 | 569 | 570 | class DELETE_GLOBAL(OpcodeWithArg): # Indexes into name list 571 | FLAGS = HAS_NAME | HAS_ARGUMENT 572 | __slots__ = () 573 | 574 | 575 | class DUP_TOPX(OpcodeWithArg): # Arg: Number of items to duplicate 576 | FLAGS = HAS_ARGUMENT 577 | __slots__ = () 578 | 579 | 580 | class LOAD_CONST(OpcodeWithArg): # Arg: Index in const list 581 | FLAGS = HAS_ARGUMENT | HAS_CONST 582 | __slots__ = () 583 | 584 | 585 | class LOAD_NAME(OpcodeWithArg): # Arg: Index in name list 586 | FLAGS = HAS_NAME | HAS_ARGUMENT 587 | __slots__ = () 588 | 589 | 590 | class BUILD_TUPLE(OpcodeWithArg): # Arg: Number of tuple items 591 | FLAGS = HAS_ARGUMENT 592 | __slots__ = () 593 | 594 | 595 | class BUILD_LIST(OpcodeWithArg): # Arg: Number of list items 596 | FLAGS = HAS_ARGUMENT 597 | __slots__ = () 598 | 599 | 600 | class BUILD_SET(OpcodeWithArg): # Arg: Number of set items 601 | FLAGS = HAS_ARGUMENT 602 | __slots__ = () 603 | 604 | 605 | class BUILD_MAP( 606 | OpcodeWithArg 607 | ): # Arg: Number of dict entries (up to 255) 608 | FLAGS = HAS_ARGUMENT 609 | __slots__ = () 610 | 611 | 612 | class LOAD_ATTR(OpcodeWithArg): # Arg: Index in name list 613 | FLAGS = HAS_NAME | HAS_ARGUMENT 614 | __slots__ = () 615 | 616 | 617 | class COMPARE_OP(OpcodeWithArg): # Arg: Comparison operator 618 | FLAGS = HAS_ARGUMENT 619 | __slots__ = () 620 | 621 | 622 | class IMPORT_NAME(OpcodeWithArg): # Arg: Index in name list 623 | FLAGS = HAS_NAME | HAS_ARGUMENT | HAS_JUNKNOWN 624 | __slots__ = () 625 | 626 | 627 | class IMPORT_FROM(OpcodeWithArg): # Arg: Index in name list 628 | FLAGS = HAS_NAME | HAS_ARGUMENT 629 | __slots__ = () 630 | 631 | 632 | class JUMP_FORWARD(OpcodeWithArg): 633 | FLAGS = HAS_JREL | HAS_ARGUMENT | NO_NEXT 634 | __slots__ = () 635 | 636 | 637 | class JUMP_IF_FALSE_OR_POP(OpcodeWithArg): 638 | FLAGS = HAS_JABS | HAS_ARGUMENT 639 | __slots__ = () 640 | 641 | 642 | class JUMP_IF_TRUE_OR_POP(OpcodeWithArg): 643 | FLAGS = HAS_JABS | HAS_ARGUMENT 644 | __slots__ = () 645 | 646 | 647 | class JUMP_ABSOLUTE(OpcodeWithArg): 648 | FLAGS = HAS_JABS | HAS_ARGUMENT | NO_NEXT 649 | __slots__ = () 650 | 651 | 652 | class POP_JUMP_IF_FALSE(OpcodeWithArg): 653 | FLAGS = HAS_JABS | HAS_ARGUMENT 654 | __slots__ = () 655 | 656 | 657 | class POP_JUMP_IF_TRUE(OpcodeWithArg): 658 | FLAGS = HAS_JABS | HAS_ARGUMENT 659 | __slots__ = () 660 | 661 | 662 | class LOAD_GLOBAL(OpcodeWithArg): # Indexes into name list 663 | FLAGS = HAS_NAME | HAS_ARGUMENT 664 | __slots__ = () 665 | 666 | 667 | class CONTINUE_LOOP(OpcodeWithArg): # Acts as jump 668 | FLAGS = HAS_JABS | HAS_ARGUMENT | NO_NEXT 669 | __slots__ = () 670 | 671 | 672 | class SETUP_LOOP(OpcodeWithArg): 673 | FLAGS = HAS_JREL | HAS_ARGUMENT | STORE_JUMP | PUSHES_BLOCK 674 | __slots__ = () 675 | 676 | 677 | class SETUP_EXCEPT(OpcodeWithArg): 678 | FLAGS = HAS_JREL | HAS_ARGUMENT | STORE_JUMP | PUSHES_BLOCK 679 | __slots__ = () 680 | 681 | 682 | class SETUP_FINALLY(OpcodeWithArg): 683 | FLAGS = HAS_JREL | HAS_ARGUMENT | STORE_JUMP | PUSHES_BLOCK 684 | __slots__ = () 685 | 686 | 687 | class LOAD_FAST(OpcodeWithArg): # Loads local variable number 688 | FLAGS = HAS_LOCAL | HAS_ARGUMENT 689 | __slots__ = () 690 | 691 | 692 | class STORE_FAST(OpcodeWithArg): # Stores local variable number 693 | FLAGS = HAS_LOCAL | HAS_ARGUMENT 694 | __slots__ = () 695 | 696 | 697 | class DELETE_FAST(OpcodeWithArg): # Deletes local variable number 698 | FLAGS = HAS_LOCAL | HAS_ARGUMENT 699 | __slots__ = () 700 | 701 | 702 | class STORE_ANNOTATION(OpcodeWithArg): 703 | FLAGS = HAS_NAME | HAS_ARGUMENT 704 | __slots__ = () 705 | 706 | 707 | class RAISE_VARARGS( 708 | OpcodeWithArg 709 | ): # Arg: Number of raise args (1, 2, or 3) 710 | FLAGS = HAS_ARGUMENT | HAS_JUNKNOWN | NO_NEXT 711 | __slots__ = () 712 | 713 | 714 | class CALL_FUNCTION(OpcodeWithArg): # Arg: #args + (#kwargs << 8) 715 | FLAGS = HAS_NARGS | HAS_ARGUMENT | HAS_JUNKNOWN 716 | __slots__ = () 717 | 718 | 719 | class MAKE_FUNCTION( 720 | OpcodeWithArg 721 | ): # Arg: Number of args with default values 722 | FLAGS = HAS_ARGUMENT 723 | __slots__ = () 724 | 725 | 726 | class BUILD_SLICE(OpcodeWithArg): # Arg: Number of items 727 | FLAGS = HAS_ARGUMENT 728 | __slots__ = () 729 | 730 | 731 | class MAKE_CLOSURE(OpcodeWithArg): 732 | FLAGS = HAS_ARGUMENT 733 | __slots__ = () 734 | 735 | 736 | class LOAD_CLOSURE(OpcodeWithArg): 737 | FLAGS = HAS_FREE | HAS_ARGUMENT 738 | __slots__ = () 739 | 740 | 741 | class LOAD_DEREF(OpcodeWithArg): 742 | FLAGS = HAS_FREE | HAS_ARGUMENT 743 | __slots__ = () 744 | 745 | 746 | class STORE_DEREF(OpcodeWithArg): 747 | FLAGS = HAS_FREE | HAS_ARGUMENT 748 | __slots__ = () 749 | 750 | 751 | class DELETE_DEREF(OpcodeWithArg): 752 | FLAGS = HAS_FREE | HAS_ARGUMENT 753 | __slots__ = () 754 | 755 | 756 | class CALL_FUNCTION_VAR(OpcodeWithArg): # Arg: #args + (#kwargs << 8) 757 | FLAGS = HAS_NARGS | HAS_ARGUMENT | HAS_JUNKNOWN 758 | __slots__ = () 759 | 760 | 761 | class CALL_FUNCTION_KW(OpcodeWithArg): # Arg: #args + (#kwargs << 8) 762 | FLAGS = HAS_NARGS | HAS_ARGUMENT | HAS_JUNKNOWN 763 | __slots__ = () 764 | 765 | 766 | class CALL_FUNCTION_VAR_KW( 767 | OpcodeWithArg 768 | ): # Arg: #args + (#kwargs << 8) 769 | FLAGS = HAS_NARGS | HAS_ARGUMENT | HAS_JUNKNOWN 770 | __slots__ = () 771 | 772 | 773 | class CALL_FUNCTION_EX(OpcodeWithArg): # Arg: flags 774 | FLAGS = HAS_ARGUMENT 775 | __slots__ = () 776 | 777 | 778 | class SETUP_WITH(OpcodeWithArg): 779 | FLAGS = HAS_JREL | HAS_ARGUMENT | STORE_JUMP | PUSHES_BLOCK 780 | __slots__ = () 781 | 782 | 783 | class EXTENDED_ARG(OpcodeWithArg): 784 | FLAGS = HAS_ARGUMENT 785 | __slots__ = () 786 | 787 | 788 | class SET_ADD(OpcodeWithArg): 789 | FLAGS = HAS_ARGUMENT 790 | __slots__ = () 791 | 792 | 793 | class MAP_ADD(OpcodeWithArg): 794 | FLAGS = HAS_ARGUMENT 795 | __slots__ = () 796 | 797 | 798 | class LOAD_CLASSDEREF(OpcodeWithArg): 799 | FLAGS = HAS_FREE | HAS_ARGUMENT 800 | __slots__ = () 801 | 802 | 803 | class BUILD_LIST_UNPACK(OpcodeWithArg): # Arg: Number of items 804 | FLAGS = HAS_ARGUMENT 805 | __slots__ = () 806 | 807 | 808 | class BUILD_MAP_UNPACK(OpcodeWithArg): # Arg: Number of items 809 | FLAGS = HAS_ARGUMENT 810 | __slots__ = () 811 | 812 | 813 | class BUILD_MAP_UNPACK_WITH_CALL( 814 | OpcodeWithArg 815 | ): # Arg: Number of items 816 | FLAGS = HAS_ARGUMENT 817 | __slots__ = () 818 | 819 | 820 | class BUILD_TUPLE_UNPACK(OpcodeWithArg): # Arg: Number of items 821 | FLAGS = HAS_ARGUMENT 822 | __slots__ = () 823 | 824 | 825 | class BUILD_SET_UNPACK(OpcodeWithArg): # Arg: Number of items 826 | FLAGS = HAS_ARGUMENT 827 | __slots__ = () 828 | 829 | 830 | class SETUP_ASYNC_WITH(OpcodeWithArg): 831 | FLAGS = HAS_JREL | HAS_ARGUMENT | STORE_JUMP | PUSHES_BLOCK 832 | __slots__ = () 833 | 834 | 835 | class FORMAT_VALUE(OpcodeWithArg): # Arg: Flags 836 | FLAGS = HAS_ARGUMENT 837 | __slots__ = () 838 | 839 | 840 | class BUILD_CONST_KEY_MAP(OpcodeWithArg): # Arg: Number of items 841 | FLAGS = HAS_ARGUMENT 842 | __slots__ = () 843 | 844 | 845 | class BUILD_STRING(OpcodeWithArg): # Arg: Number of items 846 | FLAGS = HAS_ARGUMENT 847 | __slots__ = () 848 | 849 | 850 | class BUILD_TUPLE_UNPACK_WITH_CALL( 851 | OpcodeWithArg 852 | ): # Arg: Number of items 853 | FLAGS = HAS_ARGUMENT 854 | __slots__ = () 855 | 856 | 857 | class LOAD_METHOD(OpcodeWithArg): # Arg: Index in name list 858 | FLAGS = HAS_NAME | HAS_ARGUMENT 859 | __slots__ = () 860 | 861 | 862 | class CALL_METHOD(OpcodeWithArg): # Arg: #args 863 | FLAGS = HAS_ARGUMENT 864 | __slots__ = () 865 | 866 | 867 | python2_mapping = { 868 | 0: STOP_CODE, # removed in Python 3 869 | 1: POP_TOP, 870 | 2: ROT_TWO, 871 | 3: ROT_THREE, 872 | 4: DUP_TOP, 873 | 5: ROT_FOUR, # becomes DUP_TOP_TWO in Python 3 874 | 9: NOP, 875 | 10: UNARY_POSITIVE, 876 | 11: UNARY_NEGATIVE, 877 | 12: UNARY_NOT, 878 | 13: UNARY_CONVERT, # removed in Python 3 879 | 15: UNARY_INVERT, 880 | 19: BINARY_POWER, 881 | 20: BINARY_MULTIPLY, 882 | 21: BINARY_DIVIDE, # removed in Python 3 883 | 22: BINARY_MODULO, 884 | 23: BINARY_ADD, 885 | 24: BINARY_SUBTRACT, 886 | 25: BINARY_SUBSCR, 887 | 26: BINARY_FLOOR_DIVIDE, 888 | 27: BINARY_TRUE_DIVIDE, 889 | 28: INPLACE_FLOOR_DIVIDE, 890 | 29: INPLACE_TRUE_DIVIDE, 891 | 30: SLICE_0, # removed in Python 3 892 | 31: SLICE_1, # removed in Python 3 893 | 32: SLICE_2, # removed in Python 3 894 | 33: SLICE_3, # removed in Python 3 895 | 40: STORE_SLICE_0, # removed in Python 3 896 | 41: STORE_SLICE_1, # removed in Python 3 897 | 42: STORE_SLICE_2, # removed in Python 3 898 | 43: STORE_SLICE_3, # removed in Python 3 899 | 50: DELETE_SLICE_0, # removed in Python 3 900 | 51: DELETE_SLICE_1, # removed in Python 3 901 | 52: DELETE_SLICE_2, # removed in Python 3 902 | 53: DELETE_SLICE_3, # removed in Python 3 903 | 54: STORE_MAP, 904 | 55: INPLACE_ADD, 905 | 56: INPLACE_SUBTRACT, 906 | 57: INPLACE_MULTIPLY, 907 | 58: INPLACE_DIVIDE, # removed in Python 3 908 | 59: INPLACE_MODULO, 909 | 60: STORE_SUBSCR, 910 | 61: DELETE_SUBSCR, 911 | 62: BINARY_LSHIFT, 912 | 63: BINARY_RSHIFT, 913 | 64: BINARY_AND, 914 | 65: BINARY_XOR, 915 | 66: BINARY_OR, 916 | 67: INPLACE_POWER, 917 | 68: GET_ITER, 918 | 70: PRINT_EXPR, 919 | 71: PRINT_ITEM, # becomes LOAD_BUILD_CLASS in Python 3 920 | 72: PRINT_NEWLINE, # becomes YIELD_FROM in Python 3 921 | 73: PRINT_ITEM_TO, 922 | 74: PRINT_NEWLINE_TO, 923 | 75: INPLACE_LSHIFT, 924 | 76: INPLACE_RSHIFT, 925 | 77: INPLACE_AND, 926 | 78: INPLACE_XOR, 927 | 79: INPLACE_OR, 928 | 80: BREAK_LOOP, 929 | 81: WITH_CLEANUP, 930 | 82: LOAD_LOCALS, # removed in Python 3 931 | 83: RETURN_VALUE, 932 | 84: IMPORT_STAR, 933 | 85: EXEC_STMT, # removed in Python 3 934 | 86: YIELD_VALUE, 935 | 87: POP_BLOCK, 936 | 88: END_FINALLY, 937 | 89: BUILD_CLASS, # becomes POP_EXCEPT in Python 3 938 | 90: STORE_NAME, 939 | 91: DELETE_NAME, 940 | 92: UNPACK_SEQUENCE, 941 | 93: FOR_ITER, 942 | 94: LIST_APPEND, # becomes UNPACK_EX in Python 3 943 | 95: STORE_ATTR, 944 | 96: DELETE_ATTR, 945 | 97: STORE_GLOBAL, 946 | 98: DELETE_GLOBAL, 947 | 99: DUP_TOPX, # removed in Python 3 948 | 100: LOAD_CONST, 949 | 101: LOAD_NAME, 950 | 102: BUILD_TUPLE, 951 | 103: BUILD_LIST, 952 | 104: BUILD_SET, 953 | 105: BUILD_MAP, 954 | 106: LOAD_ATTR, 955 | 107: COMPARE_OP, 956 | 108: IMPORT_NAME, 957 | 109: IMPORT_FROM, 958 | 110: JUMP_FORWARD, 959 | 111: JUMP_IF_FALSE_OR_POP, 960 | 112: JUMP_IF_TRUE_OR_POP, 961 | 113: JUMP_ABSOLUTE, 962 | 114: POP_JUMP_IF_FALSE, 963 | 115: POP_JUMP_IF_TRUE, 964 | 116: LOAD_GLOBAL, 965 | 119: CONTINUE_LOOP, 966 | 120: SETUP_LOOP, 967 | 121: SETUP_EXCEPT, 968 | 122: SETUP_FINALLY, 969 | 124: LOAD_FAST, 970 | 125: STORE_FAST, 971 | 126: DELETE_FAST, 972 | 130: RAISE_VARARGS, 973 | 131: CALL_FUNCTION, 974 | 132: MAKE_FUNCTION, 975 | 133: BUILD_SLICE, 976 | 134: MAKE_CLOSURE, 977 | 135: LOAD_CLOSURE, 978 | 136: LOAD_DEREF, 979 | 137: STORE_DEREF, 980 | 140: CALL_FUNCTION_VAR, 981 | 141: CALL_FUNCTION_KW, 982 | 142: CALL_FUNCTION_VAR_KW, 983 | 143: SETUP_WITH, 984 | 145: EXTENDED_ARG, # moved to 144 in Python 3, 145 becomes LIST_APPEND 985 | 146: SET_ADD, 986 | 147: MAP_ADD, 987 | } 988 | 989 | python3_mapping = { 990 | 1: POP_TOP, 991 | 2: ROT_TWO, 992 | 3: ROT_THREE, 993 | 4: DUP_TOP, 994 | 5: DUP_TOP_TWO, 995 | 9: NOP, 996 | 10: UNARY_POSITIVE, 997 | 11: UNARY_NEGATIVE, 998 | 12: UNARY_NOT, 999 | 15: UNARY_INVERT, 1000 | 19: BINARY_POWER, 1001 | 20: BINARY_MULTIPLY, 1002 | 22: BINARY_MODULO, 1003 | 23: BINARY_ADD, 1004 | 24: BINARY_SUBTRACT, 1005 | 25: BINARY_SUBSCR, 1006 | 26: BINARY_FLOOR_DIVIDE, 1007 | 27: BINARY_TRUE_DIVIDE, 1008 | 28: INPLACE_FLOOR_DIVIDE, 1009 | 29: INPLACE_TRUE_DIVIDE, 1010 | 50: GET_AITER, 1011 | 51: GET_ANEXT, 1012 | 52: BEFORE_ASYNC_WITH, 1013 | 54: STORE_MAP, # removed in Python 3.5.2 1014 | 55: INPLACE_ADD, 1015 | 56: INPLACE_SUBTRACT, 1016 | 57: INPLACE_MULTIPLY, 1017 | 59: INPLACE_MODULO, 1018 | 60: STORE_SUBSCR, 1019 | 61: DELETE_SUBSCR, 1020 | 62: BINARY_LSHIFT, 1021 | 63: BINARY_RSHIFT, 1022 | 64: BINARY_AND, 1023 | 65: BINARY_XOR, 1024 | 66: BINARY_OR, 1025 | 67: INPLACE_POWER, 1026 | 68: GET_ITER, 1027 | 69: STORE_LOCALS, # removed in Python 3.4 1028 | 70: PRINT_EXPR, 1029 | 71: LOAD_BUILD_CLASS, # PRINT_ITEM in Python 2 1030 | 72: YIELD_FROM, # PRINT_NEWLINE in Python 2 1031 | 73: GET_AWAITABLE, 1032 | 75: INPLACE_LSHIFT, 1033 | 76: INPLACE_RSHIFT, 1034 | 77: INPLACE_AND, 1035 | 78: INPLACE_XOR, 1036 | 79: INPLACE_OR, 1037 | 80: BREAK_LOOP, 1038 | 81: WITH_CLEANUP, 1039 | 83: RETURN_VALUE, 1040 | 84: IMPORT_STAR, 1041 | 86: YIELD_VALUE, 1042 | 87: POP_BLOCK, 1043 | 88: END_FINALLY, 1044 | 89: POP_EXCEPT, # BUILD_CLASS in Python 2 1045 | 90: STORE_NAME, 1046 | 91: DELETE_NAME, 1047 | 92: UNPACK_SEQUENCE, 1048 | 93: FOR_ITER, 1049 | 94: UNPACK_EX, # LIST_APPEND in Python 2 1050 | 95: STORE_ATTR, 1051 | 96: DELETE_ATTR, 1052 | 97: STORE_GLOBAL, 1053 | 98: DELETE_GLOBAL, 1054 | 100: LOAD_CONST, 1055 | 101: LOAD_NAME, 1056 | 102: BUILD_TUPLE, 1057 | 103: BUILD_LIST, 1058 | 104: BUILD_SET, 1059 | 105: BUILD_MAP, 1060 | 106: LOAD_ATTR, 1061 | 107: COMPARE_OP, 1062 | 108: IMPORT_NAME, 1063 | 109: IMPORT_FROM, 1064 | 110: JUMP_FORWARD, 1065 | 111: JUMP_IF_FALSE_OR_POP, 1066 | 112: JUMP_IF_TRUE_OR_POP, 1067 | 113: JUMP_ABSOLUTE, 1068 | 114: POP_JUMP_IF_FALSE, 1069 | 115: POP_JUMP_IF_TRUE, 1070 | 116: LOAD_GLOBAL, 1071 | 119: CONTINUE_LOOP, 1072 | 120: SETUP_LOOP, 1073 | 121: SETUP_EXCEPT, 1074 | 122: SETUP_FINALLY, 1075 | 124: LOAD_FAST, 1076 | 125: STORE_FAST, 1077 | 126: DELETE_FAST, 1078 | 130: RAISE_VARARGS, 1079 | 131: CALL_FUNCTION, 1080 | 132: MAKE_FUNCTION, 1081 | 133: BUILD_SLICE, 1082 | 134: MAKE_CLOSURE, 1083 | 135: LOAD_CLOSURE, 1084 | 136: LOAD_DEREF, 1085 | 137: STORE_DEREF, 1086 | 138: DELETE_DEREF, 1087 | 140: CALL_FUNCTION_VAR, # removed in Python 3.6 1088 | 141: CALL_FUNCTION_KW, 1089 | 142: CALL_FUNCTION_VAR_KW, 1090 | 143: SETUP_WITH, 1091 | 144: EXTENDED_ARG, # 145 in Python 2 1092 | 145: LIST_APPEND, # 94 in Python 2 1093 | 146: SET_ADD, 1094 | 147: MAP_ADD, 1095 | 148: LOAD_CLASSDEREF, # not in Python 2 1096 | 154: SETUP_ASYNC_WITH, 1097 | } 1098 | 1099 | 1100 | def _overlay_mapping(mapping, new_entries): 1101 | ret = mapping.copy() 1102 | ret.update(new_entries) 1103 | return {k: v for k, v in ret.items() if v is not None} 1104 | 1105 | 1106 | python_3_5_mapping = _overlay_mapping( 1107 | python3_mapping, 1108 | { 1109 | 16: BINARY_MATRIX_MULTIPLY, 1110 | 17: INPLACE_MATRIX_MULTIPLY, 1111 | 54: None, 1112 | 69: GET_YIELD_FROM_ITER, # STORE_LOCALS in Python 3.3 1113 | 81: WITH_CLEANUP_START, # WITH_CLEANUP in Python 3.4 1114 | 82: WITH_CLEANUP_FINISH, 1115 | 149: BUILD_LIST_UNPACK, 1116 | 150: BUILD_MAP_UNPACK, 1117 | 151: BUILD_MAP_UNPACK_WITH_CALL, 1118 | 152: BUILD_TUPLE_UNPACK, 1119 | 153: BUILD_SET_UNPACK, 1120 | }, 1121 | ) 1122 | 1123 | python_3_6_mapping = _overlay_mapping( 1124 | python_3_5_mapping, 1125 | { 1126 | 85: SETUP_ANNOTATIONS, 1127 | 127: STORE_ANNOTATION, # removed in Python 3.7 1128 | 140: None, 1129 | 142: CALL_FUNCTION_EX, # CALL_FUNCTION_VAR_KW in Python 3.5 1130 | 155: FORMAT_VALUE, 1131 | 156: BUILD_CONST_KEY_MAP, 1132 | 157: BUILD_STRING, 1133 | 158: BUILD_TUPLE_UNPACK_WITH_CALL, 1134 | }, 1135 | ) 1136 | 1137 | python_3_7_mapping = _overlay_mapping( 1138 | python_3_6_mapping, 1139 | {127: None, 160: LOAD_METHOD, 161: CALL_METHOD}, 1140 | ) 1141 | 1142 | 1143 | class _LineNumberTableParser(object): 1144 | """State machine for decoding a Python line number array.""" 1145 | 1146 | def __init__(self, lnotab, firstlineno): 1147 | assert not len(lnotab) % 2 # lnotab always has an even number of elements 1148 | self.lnotab = lnotab 1149 | self.lineno = firstlineno 1150 | self.next_addr = lnotab[0] if lnotab else 0 1151 | self.pos = 0 1152 | 1153 | def get(self, i): 1154 | """Get the line number for the instruction at the given position. 1155 | 1156 | This does NOT allow random access. Call with incremental numbers. 1157 | 1158 | Args: 1159 | i: The byte position in the bytecode. i needs to stay constant or increase 1160 | between calls. 1161 | 1162 | Returns: 1163 | The line number corresponding to the position at i. 1164 | """ 1165 | while i >= self.next_addr and self.pos < len(self.lnotab): 1166 | self.lineno += self.lnotab[self.pos + 1] 1167 | self.pos += 2 1168 | if self.pos < len(self.lnotab): 1169 | self.next_addr += self.lnotab[self.pos] 1170 | return self.lineno 1171 | 1172 | 1173 | def _prettyprint_arg( 1174 | cls, oparg, co_consts, co_names, co_varnames, cellvars_freevars 1175 | ): 1176 | """Prettyprint `oparg`.""" 1177 | if cls.has_jrel(): 1178 | return oparg 1179 | elif co_consts and cls.has_const(): 1180 | return repr(co_consts[oparg]) 1181 | elif co_names and cls.has_name(): 1182 | return co_names[oparg] 1183 | elif co_varnames and cls.has_local(): 1184 | return co_varnames[oparg] 1185 | elif cellvars_freevars and cls.has_free(): 1186 | return cellvars_freevars[oparg] 1187 | else: 1188 | return oparg 1189 | 1190 | 1191 | def _bytecode_reader(data, mapping): 1192 | """Reads binary data from pyc files as bytecode. 1193 | 1194 | Works with Python3.5 and below. 1195 | 1196 | Arguments: 1197 | data: The block of binary pyc code 1198 | mapping: {opcode : class} 1199 | 1200 | Yields: 1201 | (start position, end position, opcode class, oparg) 1202 | """ 1203 | assert isinstance(data, bytes) 1204 | pos = 0 1205 | extended_arg = 0 1206 | start = 0 1207 | size = len(data) 1208 | byte_at = lambda i: data[i] 1209 | while pos < size: 1210 | opcode = byte_at(pos) 1211 | cls = mapping[opcode] 1212 | oparg = None 1213 | if cls is EXTENDED_ARG: 1214 | # EXTENDED_ARG modifies the opcode after it, setting bits 16..31 of 1215 | # its argument. 1216 | assert not extended_arg, "two EXTENDED_ARGs in a row" 1217 | extended_arg = ( 1218 | byte_at(pos + 1) << 16 | byte_at(pos + 2) << 24 1219 | ) 1220 | bytes_read = 3 1221 | elif cls.FLAGS & HAS_ARGUMENT: 1222 | oparg = ( 1223 | byte_at(pos + 1) 1224 | | byte_at(pos + 2) << 8 1225 | | extended_arg 1226 | ) 1227 | extended_arg = 0 1228 | bytes_read = 3 1229 | else: 1230 | assert ( 1231 | not extended_arg 1232 | ), "EXTENDED_ARG in front of opcode without arg" 1233 | extended_arg = 0 1234 | bytes_read = 1 1235 | # Don't yield EXTENDED_ARG; it is part of the next opcode. 1236 | if cls is not EXTENDED_ARG: 1237 | yield (start, pos + bytes_read, cls, oparg) 1238 | start = pos + bytes_read 1239 | pos += bytes_read 1240 | 1241 | 1242 | def _wordcode_reader(data, mapping): 1243 | """Reads binary data from pyc files as wordcode. 1244 | 1245 | Works with Python3.6 and above. 1246 | 1247 | Arguments: 1248 | data: The block of binary pyc code 1249 | mapping: {opcode : class} 1250 | 1251 | Yields: 1252 | (start position, end position, opcode class, oparg) 1253 | """ 1254 | assert isinstance(data, bytes) 1255 | extended_arg = 0 1256 | start = 0 1257 | byte_at = lambda i: data[i] 1258 | for pos in range(0, len(data), 2): 1259 | opcode = byte_at(pos) 1260 | cls = mapping[opcode] 1261 | if cls is EXTENDED_ARG: 1262 | oparg = byte_at(pos + 1) | extended_arg 1263 | extended_arg = oparg << 8 1264 | elif cls.FLAGS & HAS_ARGUMENT: 1265 | oparg = byte_at(pos + 1) | extended_arg 1266 | extended_arg = 0 1267 | else: 1268 | oparg = None 1269 | extended_arg = 0 1270 | # Don't yield EXTENDED_ARG; it is part of the next opcode. 1271 | if cls is not EXTENDED_ARG: 1272 | yield (start, pos + 2, cls, oparg) 1273 | start = pos + 2 1274 | 1275 | 1276 | def _dis( 1277 | data, 1278 | mapping, 1279 | reader, 1280 | co_varnames=None, 1281 | co_names=None, 1282 | co_consts=None, 1283 | co_cellvars=None, 1284 | co_freevars=None, 1285 | co_lnotab=None, 1286 | co_firstlineno=None, 1287 | ): 1288 | """Disassemble a string into a list of Opcode instances.""" 1289 | code = [] 1290 | lp = ( 1291 | _LineNumberTableParser(co_lnotab, co_firstlineno) 1292 | if co_lnotab 1293 | else None 1294 | ) 1295 | offset_to_index = {} 1296 | if co_cellvars is not None and co_freevars is not None: 1297 | cellvars_freevars = co_cellvars + co_freevars 1298 | else: 1299 | cellvars_freevars = None 1300 | for pos, end_pos, cls, oparg in reader(data, mapping): 1301 | index = len(code) 1302 | offset_to_index[pos] = index 1303 | if lp: 1304 | line = lp.get(pos) 1305 | else: 1306 | # single line programs don't have co_lnotab 1307 | line = co_firstlineno 1308 | if oparg is not None: 1309 | if cls.has_jrel(): 1310 | oparg += end_pos 1311 | pretty = _prettyprint_arg( 1312 | cls, 1313 | oparg, 1314 | co_consts, 1315 | co_names, 1316 | co_varnames, 1317 | cellvars_freevars, 1318 | ) 1319 | code.append( 1320 | cls(index, line, oparg, pretty) 1321 | ) # pytype: disable=wrong-arg-count 1322 | else: 1323 | code.append(cls(index, line)) 1324 | 1325 | # Map the target of jump instructions to the opcode they jump to, and fill 1326 | # in "next" and "prev" pointers 1327 | for i, op in enumerate(code): 1328 | if op.FLAGS & (HAS_JREL | HAS_JABS): 1329 | op.arg = op.pretty_arg = offset_to_index[op.arg] 1330 | op.target = code[op.arg] 1331 | op.prev = code[i - 1] if i > 0 else None 1332 | op.next = code[i + 1] if i < len(code) - 1 else None 1333 | return code 1334 | 1335 | 1336 | def dis(data, python_version, *args, **kwargs): 1337 | """Set up version-specific arguments and call _dis().""" 1338 | major, minor = python_version[0], python_version[1] 1339 | assert major in (2, 3) 1340 | mapping = { 1341 | (2, 7): python2_mapping, 1342 | (3, 4): python3_mapping, 1343 | (3, 5): python_3_5_mapping, 1344 | (3, 6): python_3_6_mapping, 1345 | (3, 7): python_3_7_mapping, 1346 | }[(major, minor)] 1347 | reader = ( 1348 | _wordcode_reader 1349 | if (major, minor) > (3, 5) 1350 | else _bytecode_reader 1351 | ) 1352 | return _dis(data, mapping, reader, *args, **kwargs) 1353 | 1354 | 1355 | def dis_code(code): 1356 | return dis( 1357 | data=code.co_code, 1358 | python_version=code.python_version, 1359 | co_varnames=code.co_varnames, 1360 | co_names=code.co_names, 1361 | co_consts=code.co_consts, 1362 | co_cellvars=code.co_cellvars, 1363 | co_freevars=code.co_freevars, 1364 | co_lnotab=code.co_lnotab, 1365 | co_firstlineno=code.co_firstlineno, 1366 | ) 1367 | -------------------------------------------------------------------------------- /unpyc/unpyc/unpyc3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Decompiler for Python3.7. 5 | Decompile a module or a function using the decompile() function 6 | 7 | >>> from unpyc3 import decompile 8 | >>> def foo(x, y, z=3, *args): 9 | ... global g 10 | ... for i, j in zip(x, y): 11 | ... if z == i + j or args[i] == j: 12 | ... g = i, j 13 | ... return 14 | ... 15 | >>> print(decompile(foo)) 16 | 17 | def foo(x, y, z=3, *args): 18 | global g 19 | for i, j in zip(x, y): 20 | if z == i + j or args[i] == j: 21 | g = i, j 22 | return 23 | >>> 24 | """ 25 | 26 | from __future__ import annotations 27 | 28 | __package__ = "unpyc" 29 | import sys 30 | import traceback 31 | from pathlib import Path 32 | from types import ModuleType 33 | nspkg = ModuleType(__package__) 34 | nspkg.__package__ = __package__ 35 | nspkg.__name__ = __package__ 36 | nspkg.__path__ = [ 37 | Path(__file__).parent.absolute().as_posix() 38 | ] 39 | sys.modules[__package__] = nspkg 40 | 41 | sys.modules[f"[__package__].{__name__}"] = __import__(__name__) 42 | from typing import Union, Iterable, Any, List 43 | from importlib.machinery import SourceFileLoader 44 | from importlib.util import module_from_spec, spec_from_loader 45 | import opcode 46 | 47 | for name in ("opcodes",): 48 | path = ( 49 | Path(__file__).parent / f"{name}.py" 50 | ).absolute().as_posix() 51 | loader = SourceFileLoader( 52 | f"{__package__}.{name}", 53 | path, 54 | ) 55 | module = module_from_spec( 56 | spec_from_loader( 57 | f"{__package__}.{name}", 58 | loader=loader, 59 | origin=path, 60 | is_package=False 61 | ) 62 | ) 63 | loader.exec_module(module) 64 | globals()[name] = module 65 | 66 | opnames = {} 67 | opmap = {} 68 | 69 | for opc, opname in enumerate(opcode.opname): 70 | opmap[opname] = opc 71 | opnames[opc] = opname 72 | 73 | for opname, code in opmap.items(): 74 | if not opname.isidentifier(): 75 | continue 76 | # sys.stderr.write(f'Adding opcode {opname=} -> {code=}\x0a') 77 | exec(f'{opname} = {code}') 78 | # sys.stderr.flush() 79 | 80 | def missing_var(name): 81 | if name not in globals(): 82 | globals()[name] = -1 83 | 84 | missing_var("END_FINALLY") 85 | missing_var("BREAK_LOOP") 86 | 87 | # __all__ = ['decompile'] 88 | 89 | 90 | 91 | def set_trace(trace_function): 92 | global current_trace 93 | current_trace = trace_function or _trace 94 | 95 | 96 | def get_trace(): 97 | global current_trace 98 | return None if current_trace == _trace else current_trace 99 | 100 | 101 | def trace(*args): 102 | global current_trace 103 | if current_trace: 104 | current_trace(*args) 105 | 106 | 107 | def _trace(*args): 108 | pass 109 | 110 | 111 | current_trace = _trace 112 | 113 | IS_NOT_310 = sys.version_info < (3, 10) 114 | 115 | # TODO: 116 | # - Support for keyword-only arguments 117 | # - Handle assert statements better 118 | # - (Partly done) Nice spacing between function/class declarations 119 | 120 | import dis 121 | from array import array 122 | from opcode import opname, opmap, HAVE_ARGUMENT, cmp_op 123 | import inspect 124 | 125 | import struct 126 | import sys 127 | 128 | # Masks for code object's co_flag attribute 129 | VARARGS = 4 130 | VARKEYWORDS = 8 131 | 132 | # Put opcode names in the global namespace 133 | for name, val in opmap.items(): 134 | globals()[name] = val 135 | 136 | # These opcodes will generate a statement. This is used in the first 137 | # pass (in Code.find_else) to find which POP_JUMP_IF_* instructions 138 | # are jumps to the else clause of an if statement 139 | stmt_opcodes = {1, 130, 137, 138, 143, 80, 83, 86, 87, 88, 89, 90, 91, 95, 96, 97, 98, 108, 109, 119, 120, 121, 122, 125, 126} 140 | 141 | # Conditional branching opcode that make up if statements and and/or 142 | # expressions 143 | pop_jump_if_opcodes = (POP_JUMP_IF_TRUE, POP_JUMP_IF_FALSE) 144 | 145 | # These opcodes indicate that a pop_jump_if_x to the address just 146 | # after them is an else-jump 147 | else_jump_opcodes = ( 148 | JUMP_FORWARD, 149 | RETURN_VALUE, 150 | JUMP_ABSOLUTE, 151 | RAISE_VARARGS, 152 | POP_TOP, 153 | ) 154 | if "SETUP_LOOP" in globals(): 155 | else_jump_opcodes += ( 156 | SETUP_LOOP, 157 | ) 158 | # These opcodes indicate for loop rather than while loop 159 | for_jump_opcodes = (GET_ITER, FOR_ITER, GET_ANEXT) 160 | 161 | 162 | def read_code(stream): 163 | # This helper is needed in order for the PEP 302 emulation to 164 | # correctly handle compiled files 165 | # Note: stream must be opened in "rb" mode 166 | import marshal 167 | 168 | if sys.version_info < (3, 4): 169 | import imp 170 | 171 | runtime_magic = imp.get_magic() 172 | else: 173 | import importlib.util 174 | 175 | runtime_magic = importlib.util.MAGIC_NUMBER 176 | 177 | magic = stream.read(4) 178 | if magic != runtime_magic: 179 | print("*** Warning: file has wrong magic number ***") 180 | 181 | flags = 0 182 | if sys.version_info >= (3, 7): 183 | flags = struct.unpack('i', stream.read(4))[0] 184 | 185 | if flags & 1: 186 | stream.read(4) 187 | stream.read(4) 188 | else: 189 | stream.read(4) # Skip timestamp 190 | if sys.version_info >= (3, 3): 191 | stream.read(4) # Skip rawsize 192 | return marshal.load(stream) 193 | 194 | 195 | def dec_module(path) -> Suite: 196 | if path.endswith(".py"): 197 | if sys.version_info < (3, 6): 198 | import imp 199 | 200 | path = imp.cache_from_source(path) 201 | else: 202 | import importlib.util 203 | 204 | path = importlib.util.cache_from_source(path) 205 | elif not path.endswith(".pyc") and not path.endswith(".pyo"): 206 | raise ValueError("path must point to a .py or .pyc file") 207 | with open(path, "rb") as stream: 208 | code_obj = read_code(stream) 209 | code = Code(code_obj) 210 | return code.get_suite( 211 | include_declarations=False, look_for_docstring=True 212 | ) 213 | 214 | 215 | def decompile(obj) -> Union[Suite, PyStatement]: 216 | """ 217 | Decompile obj if it is a module object, a function or a 218 | code object. If obj is a string, it is assumed to be the path 219 | to a python module. 220 | """ 221 | if isinstance(obj, str): 222 | return dec_module(obj) 223 | if inspect.iscode(obj): 224 | code = Code(obj) 225 | return code.get_suite() 226 | if inspect.isfunction(obj): 227 | code = Code(obj.__code__) 228 | defaults = obj.__defaults__ 229 | kwdefaults = obj.__kwdefaults__ 230 | return DefStatement( 231 | code, defaults, kwdefaults, obj.__closure__ 232 | ) 233 | elif inspect.ismodule(obj): 234 | return dec_module(obj.__file__) 235 | else: 236 | msg = "Object must be string, module, function or code object" 237 | raise TypeError(msg) 238 | 239 | 240 | class Indent: 241 | def __init__(self, indent_level=0, indent_step=4): 242 | self.level = indent_level 243 | self.step = indent_step 244 | 245 | def write(self, pattern, *args, **kwargs): 246 | if args or kwargs: 247 | pattern = pattern.format(*args, **kwargs) 248 | return self.indent(pattern) 249 | 250 | def __add__(self, indent_increase): 251 | return type(self)(self.level + indent_increase, self.step) 252 | 253 | 254 | class IndentPrint(Indent): 255 | def indent(self, string): 256 | print(" " * self.step * self.level + string) 257 | 258 | 259 | class IndentString(Indent): 260 | def __init__(self, indent_level=0, indent_step=4, lines=None): 261 | Indent.__init__(self, indent_level, indent_step) 262 | self.lines = [] if lines is None else lines 263 | 264 | def __add__(self, indent_increase): 265 | return type(self)( 266 | self.level + indent_increase, self.step, self.lines 267 | ) 268 | 269 | def sep(self): 270 | if not self.lines or self.lines[-1]: 271 | self.lines.append("") 272 | 273 | def indent(self, string): 274 | self.lines.append(" " * self.step * self.level + string) 275 | 276 | def __str__(self): 277 | return "\n".join(self.lines) 278 | 279 | 280 | class Stack: 281 | def __init__(self): 282 | self._stack = [] 283 | self._counts = {} 284 | 285 | def __bool__(self): 286 | return bool(self._stack) 287 | 288 | def __len__(self): 289 | return len(self._stack) 290 | 291 | def __contains__(self, val): 292 | return self.get_count(val) > 0 293 | 294 | def get_count(self, obj): 295 | return self._counts.get(id(obj), 0) 296 | 297 | def set_count(self, obj, val): 298 | if val: 299 | self._counts[id(obj)] = val 300 | else: 301 | del self._counts[id(obj)] 302 | 303 | def pop1(self): 304 | val = None 305 | if self._stack: 306 | val = self._stack.pop() 307 | else: 308 | raise Exception('Empty stack popped!') 309 | self.set_count(val, self.get_count(val) - 1) 310 | return val 311 | 312 | def pop(self, count=None): 313 | if count is None: 314 | return self.pop1() 315 | vals = [self.pop1() for _ in range(count)] 316 | vals.reverse() 317 | return vals 318 | 319 | def push(self, *args): 320 | for val in args: 321 | self.set_count(val, self.get_count(val) + 1) 322 | self._stack.append(val) 323 | 324 | def peek(self, count=None): 325 | return self._stack[-1] if count is None else self._stack[-count:] 326 | 327 | def __getitem__(self, x): 328 | res = Stack() 329 | res._stack = self._stack[x] 330 | _counts = {} 331 | for val in res._stack: 332 | _counts[id(val)] = _counts.get(id(val), 0) + 1 333 | res._counts = _counts 334 | return res 335 | 336 | 337 | def code_walker(code): 338 | l = len(code) 339 | code = array('B', code) 340 | oparg = 0 341 | i = 0 342 | extended_arg = 0 343 | 344 | while i < l: 345 | op = code[i] 346 | offset = 1 347 | if sys.version_info >= (3, 6): 348 | oparg = code[i + offset] 349 | offset += 1 350 | elif op >= HAVE_ARGUMENT: 351 | oparg = ( 352 | code[i + offset] 353 | + code[i + offset + 1] * 256 354 | + extended_arg 355 | ) 356 | extended_arg = 0 357 | offset += 2 358 | if op == EXTENDED_ARG: 359 | if sys.version_info >= (3, 6): 360 | op = code[i + offset] 361 | offset += 1 362 | oparg <<= 8 363 | oparg |= code[i + offset] 364 | offset += 1 365 | else: 366 | extended_arg = oparg * 65536 367 | yield i, (op, oparg) 368 | i += offset 369 | 370 | 371 | class CodeFlags(object): 372 | def __init__(self, cf): 373 | self.flags = cf 374 | 375 | @property 376 | def optimized(self): 377 | return self.flags & 0x1 378 | 379 | @property 380 | def new_local(self): 381 | return self.flags & 0x2 382 | 383 | @property 384 | def varargs(self): 385 | return self.flags & 0x4 386 | 387 | @property 388 | def varkwargs(self): 389 | return self.flags & 0x8 390 | 391 | @property 392 | def nested(self): 393 | return self.flags & 0x10 394 | 395 | @property 396 | def generator(self): 397 | return self.flags & 0x20 398 | 399 | @property 400 | def no_free(self): 401 | return self.flags & 0x40 402 | 403 | @property 404 | def coroutine(self): 405 | return self.flags & 0x80 406 | 407 | @property 408 | def iterable_coroutine(self): 409 | return self.flags & 0x100 410 | 411 | @property 412 | def async_generator(self): 413 | return self.flags & 0x200 414 | 415 | 416 | class Code: 417 | def __init__(self, code_obj, parent=None, is_comp=False): 418 | self.code_obj = code_obj 419 | self.parent = parent 420 | self.derefnames = [ 421 | PyName(v) 422 | for v in code_obj.co_cellvars + code_obj.co_freevars 423 | ] 424 | self.consts = list(map(PyConst, code_obj.co_consts)) 425 | self.names = list(map(PyName, code_obj.co_names)) 426 | self.varnames = list(map(PyName, code_obj.co_varnames)) 427 | self.instr_seq = list(code_walker(code_obj.co_code)) 428 | self.instr_list = self.instr_seq 429 | self.instr_map = { 430 | addr: i for i, (addr, _) in enumerate(self.instr_seq) 431 | } 432 | self.name = code_obj.co_name 433 | self.globals = [] 434 | self.nonlocals = [] 435 | self.jump_targets = [] 436 | self.find_else() 437 | self.find_jumps() 438 | trace('================================================') 439 | trace(self.code_obj) 440 | trace('================================================') 441 | for addr in self: 442 | trace(str(addr)) 443 | if ( 444 | addr.opcode in stmt_opcodes 445 | or addr.opcode in pop_jump_if_opcodes 446 | ): 447 | trace(' ') 448 | trace('================================================') 449 | self.flags: CodeFlags = CodeFlags(code_obj.co_flags) 450 | self.is_comp = is_comp 451 | 452 | def __getitem__(self, instr_index): 453 | if 0 <= instr_index < len(self.instr_seq): 454 | return Address(self, instr_index) 455 | 456 | def __iter__(self): 457 | for i in range(len(self.instr_seq)): 458 | yield Address(self, i) 459 | 460 | def show(self): 461 | for addr in self: 462 | print(addr) 463 | 464 | def address(self, addr): 465 | if addr in self.instr_map: 466 | tgt_addr = self.instr_map[addr] 467 | ret = self[tgt_addr] 468 | return ret 469 | if addr in self.instr_list: 470 | tgt_addr = self.instr_list[addr] 471 | ret = self[tgt_addr] 472 | return ret 473 | return self[addr] 474 | 475 | def iscellvar(self, i): 476 | return i < len(self.code_obj.co_cellvars) 477 | 478 | def find_jumps(self): 479 | for addr in self: 480 | opcode, arg = addr 481 | if jt := addr.jump(): 482 | self.jump_targets.append(jt) 483 | 484 | def find_else(self): 485 | jumps = {} 486 | last_jump = None 487 | for addr in self: 488 | try: 489 | opcode, arg = addr 490 | if opcode in pop_jump_if_opcodes: 491 | # 3.10 needs a doubled arg (e.g. 14) but any 492 | # version lower does not (e.g. 28) 493 | jump_addr = self.address(arg * (2 - IS_NOT_310) - 2) 494 | if ( 495 | jump_addr.opcode in else_jump_opcodes 496 | or jump_addr.opcode is FOR_ITER 497 | ): 498 | last_jump = addr 499 | jumps[jump_addr] = addr 500 | elif opcode == JUMP_ABSOLUTE: 501 | # This case is to deal with some nested ifs such as: 502 | # if a: 503 | # if b: 504 | # f() 505 | # elif c: 506 | # g() 507 | jump_addr = self.address(arg) 508 | if jump_addr in jumps: 509 | jumps[addr] = jumps[jump_addr] 510 | elif opcode == JUMP_FORWARD: 511 | jump_addr = addr[1] + arg 512 | if jump_addr in jumps: 513 | jumps[addr] = jumps[jump_addr] 514 | elif opcode in stmt_opcodes and last_jump is not None: 515 | # This opcode will generate a statement, so it means 516 | # that the last POP_JUMP_IF_x was an else-jump 517 | jumps[addr] = last_jump 518 | except: 519 | pass 520 | self.else_jumps = set(jumps.values()) 521 | 522 | def get_suite( 523 | self, include_declarations=True, look_for_docstring=False 524 | ) -> Suite: 525 | dec = SuiteDecompiler(self[0]) 526 | dec.run() 527 | first_stmt = dec.suite and dec.suite[0] 528 | # Change __doc__ = "docstring" to "docstring" 529 | if look_for_docstring and isinstance( 530 | first_stmt, AssignStatement 531 | ): 532 | chain = first_stmt.chain 533 | if len(chain) == 2 and str(chain[0]) == "__doc__": 534 | dec.suite[0] = DocString(first_stmt.chain[1].val) 535 | if not include_declarations or not self.globals and not self.nonlocals: 536 | return dec.suite 537 | suite = Suite() 538 | if self.globals: 539 | stmt = "global " + ", ".join(map(str, self.globals)) 540 | suite.add_statement(SimpleStatement(stmt)) 541 | if self.nonlocals: 542 | stmt = "nonlocal " + ", ".join( 543 | map(str, self.nonlocals) 544 | ) 545 | suite.add_statement(SimpleStatement(stmt)) 546 | for stmt in dec.suite: 547 | suite.add_statement(stmt) 548 | return suite 549 | 550 | def declare_global(self, name): 551 | """ 552 | Declare name as a global. Called by STORE_GLOBAL and 553 | DELETE_GLOBAL 554 | """ 555 | if not self.is_comp and name not in self.globals: 556 | self.globals.append(name) 557 | 558 | def ensure_global(self, name): 559 | """ 560 | Declare name as global only if it is also a local variable 561 | name in one of the surrounding code objects. This is called 562 | by LOAD_GLOBAL 563 | """ 564 | parent = self.parent 565 | while parent: 566 | if name in parent.varnames: 567 | return self.declare_global(name) 568 | parent = parent.parent 569 | 570 | def declare_nonlocal(self, name): 571 | """ 572 | Declare name as nonlocal. Called by STORE_DEREF and 573 | DELETE_DEREF (but only when the name denotes a free variable, 574 | not a cell one). 575 | """ 576 | if not self.is_comp and name not in self.nonlocals: 577 | self.nonlocals.append(name) 578 | 579 | 580 | class Address: 581 | def __init__(self, code, instr_index): 582 | self.code = code 583 | self.index = instr_index 584 | self.addr, (self.opcode, self.arg) = code.instr_seq[ 585 | instr_index 586 | ] 587 | 588 | def __eq__(self, other): 589 | return ( 590 | isinstance(other, type(self)) 591 | and self.code == other.code 592 | and self.index == other.index 593 | ) 594 | 595 | def __lt__(self, other): 596 | return other is None or ( 597 | isinstance(other, type(self)) 598 | and self.code == other.code 599 | and self.index < other.index 600 | ) 601 | 602 | def __str__(self): 603 | mark = "* " if self in self.code.else_jumps else " " 604 | jump = self.jump() 605 | jt = '>>' if self.is_jump_target else ' ' 606 | arg = self.arg or " " 607 | jdest = f'\t(to {jump.addr})' if jump and jump.addr != self.arg else '' 608 | val = '' 609 | op = opname[self.opcode].ljust(18, ' ') 610 | try: 611 | 612 | val = ( 613 | self.code.globals[self.arg] 614 | and self.arg + 1 < len(self.code.globals) 615 | if 'GLOBAL' in op 616 | else self.code.names[self.arg] 617 | if 'ATTR' in op 618 | else self.code.names[self.arg] 619 | if 'NAME' in op 620 | else self.code.names[self.arg] 621 | if 'LOAD_METHOD' in op 622 | else self.code.consts[self.arg] 623 | if 'CONST' in op 624 | else self.code.varnames[self.arg] 625 | if 'FAST' in op 626 | else self.code.derefnames[self.arg] 627 | if 'DEREF' in op 628 | else cmp_op[self.arg] 629 | if 'COMPARE' in op 630 | else '' 631 | ) 632 | if val != '': 633 | val = f'\t({val})' 634 | except: 635 | pass 636 | 637 | return f"{jt}{mark}\t{self.addr}\t{op}\t{arg}{jdest}{val}" 638 | 639 | def __add__(self, delta): 640 | return self.code.address(self.addr + delta) 641 | 642 | def __getitem__(self, index) -> Address: 643 | return self.code[self.index + index] 644 | 645 | def __iter__(self): 646 | yield self.opcode 647 | yield self.arg 648 | 649 | def __hash__(self): 650 | return hash((self.code, self.index)) 651 | 652 | @property 653 | def is_else_jump(self): 654 | return self in self.code.else_jumps 655 | 656 | @property 657 | def is_jump_target(self): 658 | return self in self.code.jump_targets 659 | 660 | def change_instr(self, opcode, arg=None): 661 | self.code.instr_seq[self.index] = (self.addr, (opcode, arg)) 662 | 663 | def jump(self) -> Address: 664 | opcode = self.opcode 665 | if opcode in dis.hasjrel: 666 | return self[self.arg // (1 + IS_NOT_310) + 1] 667 | elif opcode in dis.hasjabs: 668 | return self.code.address(self.arg * (2 - IS_NOT_310)) 669 | 670 | def seek( 671 | self, opcode: Iterable, increment: int, end: Address = None 672 | ) -> Address: 673 | if not isinstance(opcode, Iterable): 674 | opcode = (opcode,) 675 | a = self[increment] 676 | while a and a != end: 677 | if a.opcode in opcode: 678 | return a 679 | a = a[increment] 680 | 681 | def seek_back( 682 | self, opcode: Union[Iterable, int], end: Address = None 683 | ) -> Address: 684 | return self.seek(opcode, -1, end) 685 | 686 | def seek_forward( 687 | self, opcode: Union[Iterable, int], end: Address = None 688 | ) -> Address: 689 | return self.seek(opcode, 1, end) 690 | 691 | def seek_back_statement( 692 | self, opcode: Union[Iterable, int] 693 | ) -> Address: 694 | last_statement = self.seek_back(stmt_opcodes) 695 | return self.seek(opcode, -1, last_statement) 696 | 697 | def seek_forward_statement( 698 | self, opcode: Union[Iterable, int] 699 | ) -> Address: 700 | next_statement = self.seek_forward(stmt_opcodes) 701 | return self.seek(opcode, 1, next_statement) 702 | 703 | 704 | class AsyncMixin: 705 | def __init__(self): 706 | self.is_async = False 707 | 708 | @property 709 | def async_prefix(self): 710 | return 'async ' if self.is_async else '' 711 | 712 | 713 | class AwaitableMixin: 714 | def __init__(self): 715 | self.is_awaited = False 716 | 717 | @property 718 | def await_prefix(self): 719 | return 'await ' if self.is_awaited else '' 720 | 721 | 722 | class PyExpr: 723 | def wrap(self, condition=True): 724 | return f"({self})" if condition else str(self) 725 | 726 | def store(self, dec, dest): 727 | chain = dec.assignment_chain 728 | chain.append(dest) 729 | if self not in dec.stack: 730 | chain.append(self) 731 | dec.suite.add_statement(AssignStatement(chain)) 732 | dec.assignment_chain = [] 733 | 734 | def on_pop(self, dec: SuiteDecompiler): 735 | if chain := dec.assignment_chain: 736 | chain = chain[::-1] 737 | chain.append(self) 738 | dec.suite.add_statement(AssignStatement(chain)) 739 | dec.assignment_chain = [] 740 | else: 741 | dec.write(str(self)) 742 | 743 | 744 | class PyConst(PyExpr): 745 | def __init__(self, val): 746 | self.val = val 747 | self.precedence = 14 if isinstance(val, int) else 100 748 | 749 | def __str__(self): 750 | return repr(self.val) 751 | 752 | def __iter__(self): 753 | return iter(self.val) 754 | 755 | def __eq__(self, other): 756 | return isinstance(other, PyConst) and self.val == other.val 757 | 758 | 759 | class PyFormatValue(PyConst): 760 | def __init__(self, val): 761 | super().__init__(val) 762 | self.formatter = '' 763 | 764 | @staticmethod 765 | def fmt(string): 766 | return f'f\'{string}\'' 767 | 768 | def base(self): 769 | return f'{{{self.val}{self.formatter}}}' 770 | 771 | def __str__(self): 772 | return self.fmt(self.base()) 773 | 774 | 775 | class PyFormatString(PyExpr): 776 | precedence = 100 777 | 778 | def __init__(self, params): 779 | super().__init__() 780 | self.params = params 781 | 782 | def __str__(self): 783 | return "f'{}'".format( 784 | ''.join( 785 | [ 786 | p.base().replace('\'', '\"') 787 | if isinstance(p, PyFormatValue) 788 | else p.name 789 | if isinstance(p, PyName) 790 | else str(p.val.encode('utf-8'))[1:] 791 | .replace('\'', '') 792 | .replace('{', '{{') 793 | .replace('}', '}}') 794 | for p in self.params 795 | ] 796 | ) 797 | ) 798 | 799 | 800 | class PyTuple(PyExpr): 801 | precedence = 0 802 | 803 | def __init__(self, values): 804 | self.values = values 805 | 806 | def __str__(self): 807 | if not self.values: 808 | return "()" 809 | valstr = [ 810 | val.wrap(val.precedence <= self.precedence) 811 | for val in self.values 812 | ] 813 | return f'({valstr[0]},)' if len(valstr) == 1 else '(' + ", ".join(valstr) + ')' 814 | 815 | def __iter__(self): 816 | return iter(self.values) 817 | 818 | def wrap(self, condition=True): 819 | return str(self) 820 | 821 | 822 | class PyList(PyExpr): 823 | precedence = 16 824 | 825 | def __init__(self, values): 826 | self.values = values 827 | 828 | def __str__(self): 829 | valstr = ", ".join( 830 | val.wrap(val.precedence <= 0) for val in self.values 831 | ) 832 | return f"[{valstr}]" 833 | 834 | def __iter__(self): 835 | return iter(self.values) 836 | 837 | 838 | class PySet(PyExpr): 839 | precedence = 16 840 | 841 | def __init__(self, values): 842 | self.values = values 843 | 844 | def __str__(self): 845 | valstr = ", ".join( 846 | val.wrap(val.precedence <= 0) for val in self.values 847 | ) 848 | return "{{{}}}".format(valstr) 849 | 850 | def __iter__(self): 851 | return iter(self.values) 852 | 853 | 854 | class PyDict(PyExpr): 855 | precedence = 16 856 | 857 | def __init__(self): 858 | self.items = [] 859 | 860 | def set_item(self, key, val): 861 | self.items.append((key, val)) 862 | 863 | def __str__(self): 864 | itemstr = ", ".join( 865 | f"{kv[0]}: {kv[1]}" if len(kv) == 2 else str(kv[0]) 866 | for kv in self.items 867 | ) 868 | return f"{{{itemstr}}}" 869 | 870 | 871 | class PyName(PyExpr, AwaitableMixin): 872 | precedence = 100 873 | 874 | def __init__(self, name): 875 | AwaitableMixin.__init__(self) 876 | self.name = name 877 | 878 | def __str__(self): 879 | return f'{self.await_prefix}{self.name}' 880 | 881 | def __eq__(self, other): 882 | return ( 883 | isinstance(other, type(self)) and self.name == other.name 884 | ) 885 | 886 | 887 | class PyUnaryOp(PyExpr): 888 | def __init__(self, operand): 889 | self.operand = operand 890 | 891 | def __str__(self): 892 | opstr = self.operand.wrap( 893 | self.operand.precedence < self.precedence 894 | ) 895 | return self.pattern.format(opstr) 896 | 897 | @classmethod 898 | def instr(cls, stack): 899 | stack.push(cls(stack.pop())) 900 | 901 | 902 | class PyBinaryOp(PyExpr): 903 | def __init__(self, left, right): 904 | self.left = left 905 | self.right = right 906 | 907 | def wrap_left(self): 908 | return self.left.wrap(self.left.precedence < self.precedence) 909 | 910 | def wrap_right(self): 911 | return self.right.wrap( 912 | self.right.precedence <= self.precedence 913 | ) 914 | 915 | def __str__(self): 916 | return self.pattern.format( 917 | self.wrap_left(), self.wrap_right() 918 | ) 919 | 920 | @classmethod 921 | def instr(cls, stack): 922 | right = stack.pop() 923 | left = stack.pop() 924 | stack.push(cls(left, right)) 925 | 926 | 927 | class PySubscript(PyBinaryOp): 928 | precedence = 15 929 | pattern = "{}[{}]" 930 | 931 | def wrap_right(self): 932 | return str(self.right) 933 | 934 | 935 | class PyInOp(PyBinaryOp, PyExpr): 936 | precedence = 15 937 | pattern = "{item}{_not} in {seq}" 938 | 939 | def __init__(self, left, right, negate): 940 | super(PyInOp, self).__init__(left, right) 941 | self.negate = negate 942 | 943 | def wrap_left(self): 944 | return self.left.wrap(self.left.precedence < self.precedence) 945 | 946 | def wrap_right(self): 947 | return self.right.wrap( 948 | self.right.precedence <= self.precedence 949 | ) 950 | 951 | def __str__(self): 952 | return self.pattern.format( 953 | item=self.wrap_left(), 954 | seq=self.wrap_right(), 955 | _not="not " if self.negate else "", 956 | ) 957 | 958 | 959 | class PyIsOp(PyBinaryOp, PyExpr): 960 | precedence = 15 961 | pattern = "{} is {}{}" 962 | 963 | def __init__(self, left, right, negate): 964 | super(PyIsOp, self).__init__(left, right) 965 | self.negate = negate 966 | 967 | def wrap_left(self): 968 | return self.left.wrap(self.left.precedence < self.precedence) 969 | 970 | def wrap_right(self): 971 | return self.right.wrap( 972 | self.right.precedence <= self.precedence 973 | ) 974 | 975 | def __str__(self): 976 | return self.pattern.format( 977 | self.wrap_left(), 978 | "not " if self.negate else "", 979 | self.wrap_right(), 980 | ) 981 | 982 | 983 | class PySlice(PyExpr): 984 | precedence = 1 985 | 986 | def __init__(self, args): 987 | assert len(args) in {2, 3} 988 | if len(args) == 2: 989 | self.start, self.stop = args 990 | self.step = None 991 | else: 992 | self.start, self.stop, self.step = args 993 | if self.start == PyConst(None): 994 | self.start = "" 995 | if self.stop == PyConst(None): 996 | self.stop = "" 997 | 998 | def __str__(self): 999 | if self.step is None: 1000 | return f"{self.start}:{self.stop}" 1001 | else: 1002 | return f"{self.start}:{self.stop}:{self.step}" 1003 | 1004 | 1005 | class PyCompare(PyExpr): 1006 | precedence = 6 1007 | 1008 | def __init__(self, complist): 1009 | self.complist = complist 1010 | 1011 | def __str__(self): 1012 | return " ".join( 1013 | x if i % 2 else x.wrap(x.precedence <= 6) 1014 | for i, x in enumerate(self.complist) 1015 | ) 1016 | 1017 | def extends(self, other): 1018 | return ( 1019 | self.complist[0] == other.complist[-1] 1020 | if isinstance(other, PyCompare) 1021 | else False 1022 | ) 1023 | 1024 | def chain(self, other): 1025 | return PyCompare(self.complist + other.complist[1:]) 1026 | 1027 | 1028 | class PyBooleanAnd(PyBinaryOp): 1029 | precedence = 4 1030 | pattern = "{} and {}" 1031 | 1032 | 1033 | class PyBooleanOr(PyBinaryOp): 1034 | precedence = 3 1035 | pattern = "{} or {}" 1036 | 1037 | 1038 | class PyIfElse(PyExpr): 1039 | precedence = 2 1040 | 1041 | def __init__(self, cond, true_expr, false_expr): 1042 | self.cond = cond 1043 | self.true_expr = true_expr 1044 | self.false_expr = false_expr 1045 | 1046 | def __str__(self): 1047 | p = self.precedence 1048 | cond_str = self.cond.wrap(self.cond.precedence <= p) 1049 | true_str = self.true_expr.wrap(self.cond.precedence <= p) 1050 | false_str = self.false_expr.wrap(self.cond.precedence < p) 1051 | return f"{true_str} if {cond_str} else {false_str}" 1052 | 1053 | 1054 | class PyAttribute(PyExpr): 1055 | precedence = 15 1056 | 1057 | def __init__(self, expr, attrname): 1058 | self.expr = expr 1059 | self.attrname = attrname 1060 | 1061 | def __str__(self): 1062 | expr_str = self.expr.wrap( 1063 | self.expr.precedence < self.precedence 1064 | ) 1065 | attrname = self.attrname 1066 | 1067 | if isinstance(self.expr, PyName) and self.expr.name == 'self': 1068 | __ = attrname.name.find('__') 1069 | if __ > 0: 1070 | attrname = PyName(self.attrname.name[__:]) 1071 | return f"{expr_str}.{attrname}" 1072 | 1073 | 1074 | class PyCallFunction(PyExpr, AwaitableMixin): 1075 | precedence = 15 1076 | 1077 | def __init__( 1078 | self, 1079 | func: PyAttribute, 1080 | args: list, 1081 | kwargs: list, 1082 | varargs=None, 1083 | varkw=None, 1084 | ): 1085 | AwaitableMixin.__init__(self) 1086 | self.func = func 1087 | self.args = args 1088 | self.kwargs = kwargs 1089 | self.varargs = ( 1090 | varargs 1091 | if not varargs or isinstance(varargs, Iterable) 1092 | else {varargs} 1093 | ) 1094 | self.varkw = ( 1095 | varkw 1096 | if not varkw or isinstance(varkw, Iterable) 1097 | else {varkw} 1098 | ) 1099 | 1100 | def __str__(self): 1101 | funcstr = self.func.wrap( 1102 | self.func.precedence < self.precedence 1103 | ) 1104 | if ( 1105 | hasattr(self.args, '__iter__') 1106 | and len(self.args) == 1 1107 | and not self.kwargs 1108 | and not self.varargs 1109 | and not self.varkw 1110 | ): 1111 | arg = self.args[0] 1112 | if isinstance(arg, PyGenExpr): 1113 | # Only one pair of brackets arount a single arg genexpr 1114 | return f"{funcstr}{arg}" 1115 | args = [x.wrap(x.precedence <= 0) for x in self.args] 1116 | if self.varargs is not None: 1117 | args.extend(f"*{varargs}" for varargs in self.varargs) 1118 | args.extend( 1119 | "{}={}".format( 1120 | str(k).replace('\'', ''), v.wrap(v.precedence <= 0) 1121 | ) 1122 | for k, v in self.kwargs 1123 | ) 1124 | if self.varkw is not None: 1125 | args.extend(f"**{varkw}" for varkw in self.varkw) 1126 | return f'{self.await_prefix}{funcstr}({", ".join(args)})' 1127 | 1128 | 1129 | class FunctionDefinition: 1130 | def __init__( 1131 | self, 1132 | code: Code, 1133 | defaults, 1134 | kwdefaults, 1135 | closure, 1136 | paramobjs=None, 1137 | annotations=None, 1138 | ): 1139 | self.code = code 1140 | self.defaults = defaults 1141 | self.kwdefaults = kwdefaults 1142 | self.closure = closure 1143 | self.paramobjs = paramobjs or {} 1144 | self.annotations = annotations or [] 1145 | 1146 | def is_coroutine(self): 1147 | return self.code.code_obj.co_flags & 0x100 1148 | 1149 | def getparams(self): 1150 | code_obj = self.code.code_obj 1151 | l = code_obj.co_argcount 1152 | params = [] 1153 | for name in code_obj.co_varnames[:l]: 1154 | if name in self.paramobjs: 1155 | params.append(f'{name}:{str(self.paramobjs[name])}') 1156 | else: 1157 | params.append(name) 1158 | if self.defaults: 1159 | for i, arg in enumerate(reversed(self.defaults)): 1160 | name = params[-i - 1] 1161 | if name in self.paramobjs: 1162 | params[-i - 1] = f"{name}:{str(self.paramobjs[name])}={arg}" 1163 | else: 1164 | params[-i - 1] = f"{name}={arg}" 1165 | kwparams = [] 1166 | if kwcount := code_obj.co_kwonlyargcount: 1167 | for i in range(kwcount): 1168 | name = code_obj.co_varnames[l + i] 1169 | if name in self.kwdefaults and name in self.paramobjs: 1170 | kwparams.append(f"{name}:{self.paramobjs[name]}={self.kwdefaults[name]}") 1171 | elif name in self.kwdefaults: 1172 | kwparams.append(f"{name}={self.kwdefaults[name]}") 1173 | else: 1174 | kwparams.append(name) 1175 | l += kwcount 1176 | if code_obj.co_flags & VARARGS: 1177 | name = code_obj.co_varnames[l] 1178 | if name in self.paramobjs: 1179 | params.append(f'*{name}:{str(self.paramobjs[name])}') 1180 | else: 1181 | params.append(f'*{name}') 1182 | l += 1 1183 | elif kwparams: 1184 | params.append("*") 1185 | params.extend(kwparams) 1186 | if code_obj.co_flags & VARKEYWORDS: 1187 | name = code_obj.co_varnames[l] 1188 | if name in self.paramobjs: 1189 | params.append(f'**{name}:{str(self.paramobjs[name])}') 1190 | else: 1191 | params.append(f'**{name}') 1192 | 1193 | return params 1194 | 1195 | def getreturn(self): 1196 | if self.paramobjs and 'return' in self.paramobjs: 1197 | return self.paramobjs['return'] 1198 | return None 1199 | 1200 | 1201 | class PyLambda(PyExpr, FunctionDefinition): 1202 | precedence = 1 1203 | 1204 | def __str__(self): 1205 | suite = self.code.get_suite() 1206 | params = ", ".join(self.getparams()) 1207 | if len(suite.statements) > 0: 1208 | 1209 | def strip_return(val): 1210 | return ( 1211 | val[len("return ") :] 1212 | if val.startswith('return') 1213 | else val 1214 | ) 1215 | 1216 | def strip_yield_none(val): 1217 | return '(yield)' if val == 'yield None' else val 1218 | 1219 | if isinstance(suite[0], IfStatement): 1220 | end = suite[1] if len(suite) > 1 else PyConst(None) 1221 | expr = "{} if {} else {}".format( 1222 | strip_return(str(suite[0].true_suite)), 1223 | str(suite[0].cond), 1224 | strip_return(str(end)), 1225 | ) 1226 | else: 1227 | expr = strip_return(str(suite[0])) 1228 | expr = strip_yield_none(expr) 1229 | else: 1230 | expr = "None" 1231 | return "lambda {}: {}".format(params, expr) 1232 | 1233 | 1234 | class PyComp(PyExpr): 1235 | """ 1236 | Abstraction for list, set, dict comprehensions and generator expressions 1237 | """ 1238 | 1239 | precedence = 16 1240 | 1241 | def __init__( 1242 | self, 1243 | code, 1244 | defaults, 1245 | kwdefaults, 1246 | closure, 1247 | paramobjs={}, 1248 | annotations=[], 1249 | ): 1250 | assert not defaults and not kwdefaults 1251 | self.code = code 1252 | code[0].change_instr(NOP) 1253 | last_i = len(code.instr_seq) - 1 1254 | code[last_i].change_instr(NOP) 1255 | self.annotations = annotations 1256 | 1257 | def set_iterable(self, iterable): 1258 | self.code.varnames[0] = iterable 1259 | 1260 | def __str__(self): 1261 | suite = self.code.get_suite() 1262 | return self.pattern.format(suite.gen_display()) 1263 | 1264 | 1265 | class PyListComp(PyComp): 1266 | pattern = "[{}]" 1267 | 1268 | 1269 | class PySetComp(PyComp): 1270 | pattern = "{{{}}}" 1271 | 1272 | 1273 | class PyKeyValue(PyBinaryOp): 1274 | """This is only to create dict comprehensions""" 1275 | 1276 | precedence = 1 1277 | pattern = "{}: {}" 1278 | 1279 | 1280 | class PyDictComp(PyComp): 1281 | pattern = "{{{}}}" 1282 | 1283 | 1284 | class PyGenExpr(PyComp): 1285 | precedence = 16 1286 | pattern = "({})" 1287 | 1288 | def __init__( 1289 | self, 1290 | code, 1291 | defaults, 1292 | kwdefaults, 1293 | closure, 1294 | paramobjs={}, 1295 | annotations=[], 1296 | ): 1297 | self.code = code 1298 | 1299 | 1300 | class PyYield(PyExpr): 1301 | precedence = 1 1302 | 1303 | def __init__(self, value): 1304 | self.value = value 1305 | 1306 | def __str__(self): 1307 | return f"yield {self.value}" 1308 | 1309 | 1310 | class PyYieldFrom(PyExpr): 1311 | precedence = 1 1312 | 1313 | def __init__(self, value): 1314 | self.value = value 1315 | 1316 | def __str__(self): 1317 | return f"yield from {self.value}" 1318 | 1319 | 1320 | class PyStarred(PyExpr): 1321 | """Used in unpacking assigments""" 1322 | 1323 | precedence = 15 1324 | 1325 | def __init__(self, expr): 1326 | self.expr = expr 1327 | 1328 | def __str__(self): 1329 | es = self.expr.wrap(self.expr.precedence < self.precedence) 1330 | return f"*{es}" 1331 | 1332 | 1333 | class PyListExtend(PyBinaryOp): 1334 | precedence = 15 1335 | pattern = "({}+{})" 1336 | 1337 | def wrap_right(self): 1338 | return str(self.right) 1339 | 1340 | 1341 | class PySetUpdate(PyBinaryOp): 1342 | precedence = 15 1343 | pattern = "{}.update({})" 1344 | 1345 | def wrap_right(self): 1346 | return str(self.right) 1347 | 1348 | 1349 | class PyDictMerge(PyBinaryOp): 1350 | precedence = 15 1351 | pattern = "dict(**{},**{})" 1352 | 1353 | def wrap_right(self): 1354 | return str(self.right) 1355 | 1356 | 1357 | class PyDictUpdate(PyBinaryOp): 1358 | precedence = 15 1359 | pattern = "{}.update({})" 1360 | 1361 | def wrap_right(self): 1362 | return str(self.right) 1363 | 1364 | 1365 | class PyNamedExpr(PyExpr): 1366 | precedence = 0 1367 | pattern = "({} := {})" 1368 | 1369 | def __init__(self, chain): 1370 | self.chain = chain 1371 | 1372 | def __str__(self): 1373 | expr = self.chain[-1] 1374 | for x in self.chain[-2::-1]: 1375 | expr = self.pattern.format(x, expr) 1376 | return expr 1377 | 1378 | code_map = { 1379 | '': PyLambda, 1380 | '': PyListComp, 1381 | '': PySetComp, 1382 | '': PyDictComp, 1383 | '': PyGenExpr, 1384 | } 1385 | 1386 | unary_ops = [ 1387 | ('UNARY_POSITIVE', 'Positive', '+{}', 13), 1388 | ('UNARY_NEGATIVE', 'Negative', '-{}', 13), 1389 | ('UNARY_NOT', 'Not', 'not {}', 5), 1390 | ('UNARY_INVERT', 'Invert', '~{}', 13), 1391 | ] 1392 | 1393 | binary_ops = [ 1394 | ('POWER', 'Power', '{}**{}', 14, '{} **= {}'), 1395 | ('MULTIPLY', 'Multiply', '{}*{}', 12, '{} *= {}'), 1396 | ('FLOOR_DIVIDE', 'FloorDivide', '{}//{}', 12, '{} //= {}'), 1397 | ('TRUE_DIVIDE', 'TrueDivide', '{}/{}', 12, '{} /= {}'), 1398 | ('MODULO', 'Modulo', '{} % {}', 12, '{} %= {}'), 1399 | ('ADD', 'Add', '{} + {}', 11, '{} += {}'), 1400 | ('SUBTRACT', 'Subtract', '{} - {}', 11, '{} -= {}'), 1401 | ('SUBSCR', 'Subscript', '{}[{}]', 15, None), 1402 | ('LSHIFT', 'LeftShift', '{} << {}', 10, '{} <<= {}'), 1403 | ('RSHIFT', 'RightShift', '{} >> {}', 10, '{} >>= {}'), 1404 | ('AND', 'And', '{} & {}', 9, '{} &= {}'), 1405 | ('XOR', 'Xor', '{} ^ {}', 8, '{} ^= {}'), 1406 | ('OR', 'Or', '{} | {}', 7, '{} |= {}'), 1407 | ('MATRIX_MULTIPLY', 'MatrixMultiply', '{} @ {}', 12, '{} @= {}'), 1408 | ] 1409 | 1410 | 1411 | class PyStatement(object): 1412 | def __str__(self): 1413 | istr = IndentString() 1414 | self.display(istr) 1415 | return str(istr) 1416 | 1417 | def wrap(self, condition=True): 1418 | if condition: 1419 | assert not condition 1420 | return f"({self})" 1421 | else: 1422 | return str(self) 1423 | 1424 | def on_pop(self, dec): 1425 | # dec.write("#ERROR: Unexpected context 'on_pop': pop on statement: ") 1426 | pass 1427 | 1428 | 1429 | class DocString(PyStatement): 1430 | def __init__(self, string): 1431 | self.string = string 1432 | 1433 | def display(self, indent): 1434 | if '\n' not in self.string: 1435 | indent.write(repr(self.string)) 1436 | else: 1437 | fence = "'''" if "'''" not in self.string else '"""' 1438 | lines = self.string.split('\n') 1439 | text = '\n'.join( 1440 | l.encode('unicode_escape') 1441 | .decode() 1442 | .replace(fence, '\\' + fence) 1443 | for l in lines 1444 | ) 1445 | docstring = "{0}{1}{0}".format(fence, text) 1446 | indent.write(docstring) 1447 | 1448 | 1449 | class AssignStatement(PyStatement): 1450 | def __init__(self, chain): 1451 | self.chain = chain 1452 | 1453 | def display(self, indent): 1454 | indent.write(" = ".join(map(str, self.chain))) 1455 | 1456 | def gen_display(self, seq=()): 1457 | expr = "{} := {}".format(*self.chain[-2:]) 1458 | for x in self.chain[-3::-1]: 1459 | expr = f"{x} := ({expr})" 1460 | return ' '.join((expr,) + seq) 1461 | 1462 | 1463 | class InPlaceOp(PyStatement): 1464 | def __init__(self, left, right): 1465 | self.right = right 1466 | self.left = left 1467 | 1468 | def store(self, dec, dest): 1469 | # assert dest is self.left 1470 | dec.suite.add_statement(self) 1471 | 1472 | def display(self, indent): 1473 | indent.write(self.pattern, self.left, self.right) 1474 | 1475 | @classmethod 1476 | def instr(cls, stack): 1477 | right = stack.pop() 1478 | left = stack.pop() 1479 | stack.push(cls(left, right)) 1480 | 1481 | 1482 | class Unpack: 1483 | precedence = 50 1484 | 1485 | def __init__(self, val, length, star_index=None): 1486 | self.val = val 1487 | self.length = length 1488 | self.star_index = star_index 1489 | self.dests = [] 1490 | 1491 | def store(self, dec, dest): 1492 | if len(self.dests) == self.star_index: 1493 | dest = PyStarred(dest) 1494 | self.dests.append(dest) 1495 | if len(self.dests) == self.length: 1496 | dec.stack.push(self.val) 1497 | dec.store(PyTuple(self.dests)) 1498 | 1499 | 1500 | class ImportStatement(PyStatement): 1501 | alias = "" 1502 | precedence = 100 1503 | 1504 | def __init__(self, name, level, fromlist): 1505 | self.name = name 1506 | self.alias = name 1507 | self.level = level 1508 | self.fromlist = fromlist 1509 | self.aslist = [] 1510 | 1511 | def store(self, dec: SuiteDecompiler, dest): 1512 | self.alias = dest 1513 | dec.suite.add_statement(self) 1514 | 1515 | def on_pop(self, dec): 1516 | dec.suite.add_statement(self) 1517 | 1518 | def display(self, indent): 1519 | if self.fromlist == PyConst(None): 1520 | name = self.name.name 1521 | alias = self.alias.name 1522 | if name == alias or name.startswith(f"{alias}."): 1523 | indent.write("import {}", name) 1524 | else: 1525 | indent.write("import {} as {}", name, alias) 1526 | elif self.fromlist == PyConst(('*',)): 1527 | indent.write("from {} import *", self.name.name) 1528 | else: 1529 | names = [] 1530 | for name, alias in zip(self.fromlist, self.aslist): 1531 | if name == alias: 1532 | names.append(name) 1533 | else: 1534 | names.append(f"{name} as {alias}") 1535 | indent.write( 1536 | "from {}{} import {}", 1537 | ''.join(['.' for _ in range(self.level.val)]), 1538 | self.name, 1539 | ", ".join(names), 1540 | ) 1541 | 1542 | 1543 | class ImportFrom: 1544 | def __init__(self, name): 1545 | self.name = name 1546 | 1547 | def store(self, dec, dest): 1548 | imp = dec.stack.peek() 1549 | assert isinstance(imp, ImportStatement) 1550 | 1551 | if imp.fromlist != PyConst(None): 1552 | 1553 | imp.aslist.append(dest.name) 1554 | else: 1555 | imp.alias = dest 1556 | 1557 | 1558 | class SimpleStatement(PyStatement): 1559 | def __init__(self, val): 1560 | assert val is not None 1561 | self.val = val 1562 | 1563 | def display(self, indent): 1564 | indent.write(self.val) 1565 | 1566 | def gen_display(self, seq=()): 1567 | return " ".join((self.val,) + seq) 1568 | 1569 | 1570 | class IfStatement(PyStatement): 1571 | def __init__(self, cond, true_suite, false_suite): 1572 | self.cond = cond 1573 | self.true_suite = true_suite 1574 | self.false_suite = false_suite 1575 | 1576 | def display(self, indent, is_elif=False): 1577 | ptn = "elif {}:" if is_elif else "if {}:" 1578 | indent.write(ptn, self.cond) 1579 | self.true_suite.display(indent + 1) 1580 | if not self.false_suite: 1581 | return 1582 | if len(self.false_suite) == 1: 1583 | stmt = self.false_suite[0] 1584 | if isinstance(stmt, IfStatement): 1585 | stmt.display(indent, is_elif=True) 1586 | return 1587 | indent.write("else:") 1588 | self.false_suite.display(indent + 1) 1589 | 1590 | def gen_display(self, seq=()): 1591 | assert not self.false_suite 1592 | s = f"if {self.cond}" 1593 | return self.true_suite.gen_display(seq + (s,)) 1594 | 1595 | 1596 | class ForStatement(PyStatement, AsyncMixin): 1597 | def __init__(self, iterable): 1598 | AsyncMixin.__init__(self) 1599 | self.iterable = iterable 1600 | self.else_body: Suite = None 1601 | 1602 | def store(self, dec, dest): 1603 | self.dest = dest 1604 | 1605 | def display(self, indent): 1606 | indent.write( 1607 | "{}for {} in {}:", 1608 | self.async_prefix, 1609 | self.dest, 1610 | self.iterable, 1611 | ) 1612 | if self.body: 1613 | self.body.display(indent + 1) 1614 | if self.else_body: 1615 | indent.write('else:') 1616 | self.else_body.display(indent + 1) 1617 | 1618 | def gen_display(self, seq=()): 1619 | s = "{}for {} in {}".format( 1620 | self.async_prefix, 1621 | self.dest, 1622 | self.iterable.wrap() 1623 | if isinstance(self.iterable, PyIfElse) 1624 | else self.iterable, 1625 | ) 1626 | return self.body.gen_display(seq + (s,)) 1627 | 1628 | 1629 | class WhileStatement(PyStatement): 1630 | def __init__(self, cond, body): 1631 | self.cond = cond 1632 | self.body = body 1633 | 1634 | def display(self, indent): 1635 | indent.write("while {}:", self.cond) 1636 | self.body.display(indent + 1) 1637 | 1638 | 1639 | class DecorableStatement(PyStatement): 1640 | def __init__(self): 1641 | self.decorators = [] 1642 | 1643 | def display(self, indent): 1644 | indent.sep() 1645 | for f in reversed(self.decorators): 1646 | indent.write("@{}", f) 1647 | self.display_undecorated(indent) 1648 | indent.sep() 1649 | 1650 | def decorate(self, f): 1651 | self.decorators.append(f) 1652 | 1653 | 1654 | class DefStatement( 1655 | FunctionDefinition, DecorableStatement, AsyncMixin 1656 | ): 1657 | def __init__( 1658 | self, 1659 | code: Code, 1660 | defaults, 1661 | kwdefaults, 1662 | closure, 1663 | paramobjs=None, 1664 | annotations=None, 1665 | ): 1666 | FunctionDefinition.__init__( 1667 | self, 1668 | code, 1669 | defaults, 1670 | kwdefaults, 1671 | closure, 1672 | paramobjs, 1673 | annotations, 1674 | ) 1675 | DecorableStatement.__init__(self) 1676 | AsyncMixin.__init__(self) 1677 | self.is_async = ( 1678 | code.flags.coroutine or code.flags.async_generator 1679 | ) 1680 | 1681 | def display_undecorated(self, indent): 1682 | paramlist = ", ".join(self.getparams()) 1683 | if result := self.getreturn(): 1684 | indent.write( 1685 | "{}def {}({}) -> {}:", 1686 | self.async_prefix, 1687 | self.code.name, 1688 | paramlist, 1689 | result, 1690 | ) 1691 | else: 1692 | indent.write( 1693 | "{}def {}({}):", 1694 | self.async_prefix, 1695 | self.code.name, 1696 | paramlist, 1697 | ) 1698 | # Assume that co_consts starts with None unless the function 1699 | # has a docstring, in which case it starts with the docstring 1700 | if self.code.consts[0] != PyConst(None): 1701 | docstring = self.code.consts[0].val 1702 | DocString(docstring).display(indent + 1) 1703 | self.code.get_suite().display(indent + 1) 1704 | 1705 | def store(self, dec, dest): 1706 | self.name = dest 1707 | dec.suite.add_statement(self) 1708 | 1709 | 1710 | class TryStatement(PyStatement): 1711 | def __init__(self, try_suite): 1712 | self.try_suite: Suite = try_suite 1713 | self.except_clauses: List[Any, str, Suite] = [] 1714 | self.else_suite: Suite = None 1715 | 1716 | def add_except_clause(self, exception_type, suite): 1717 | self.except_clauses.append([exception_type, None, suite]) 1718 | 1719 | def store(self, dec, dest): 1720 | self.except_clauses[-1][1] = dest 1721 | 1722 | def display(self, indent): 1723 | indent.write("try:") 1724 | self.try_suite.display(indent + 1) 1725 | for type, name, suite in self.except_clauses: 1726 | if type is None: 1727 | indent.write("except:") 1728 | elif name is None: 1729 | indent.write("except {}:", type) 1730 | else: 1731 | indent.write("except {} as {}:", type, name) 1732 | suite.display(indent + 1) 1733 | if self.else_suite: 1734 | indent.write('else:') 1735 | self.else_suite.display(indent + 1) 1736 | 1737 | 1738 | class FinallyStatement(PyStatement): 1739 | def __init__(self, try_suite, finally_suite): 1740 | self.try_suite = try_suite 1741 | self.finally_suite = finally_suite 1742 | 1743 | def display(self, indent): 1744 | # Wrap the try suite in a TryStatement if necessary 1745 | try_stmt = None 1746 | if len(self.try_suite) == 1: 1747 | try_stmt = self.try_suite[0] 1748 | if not isinstance(try_stmt, TryStatement): 1749 | try_stmt = None 1750 | if try_stmt is None: 1751 | try_stmt = TryStatement(self.try_suite) 1752 | try_stmt.display(indent) 1753 | indent.write("finally:") 1754 | self.finally_suite.display(indent + 1) 1755 | 1756 | 1757 | class WithStatement(PyStatement): 1758 | def __init__(self, with_expr): 1759 | self.with_expr = with_expr 1760 | self.with_name = None 1761 | self.is_async = False 1762 | 1763 | @property 1764 | def async_prefix(self): 1765 | return 'async ' if self.is_async else '' 1766 | 1767 | def store(self, dec, dest): 1768 | self.with_name = dest 1769 | 1770 | def display(self, indent, args=None): 1771 | # args to take care of nested withs: 1772 | # with x as t: 1773 | # with y as u: 1774 | # 1775 | # ---> 1776 | # with x as t, y as u: 1777 | # 1778 | if args is None: 1779 | args = [] 1780 | if self.with_name is None: 1781 | args.append(str(self.with_expr)) 1782 | else: 1783 | args.append(f"{self.with_expr} as {self.with_name}") 1784 | if len(self.suite) == 1 and isinstance( 1785 | self.suite[0], WithStatement 1786 | ): 1787 | self.suite[0].display(indent, args) 1788 | else: 1789 | indent.write( 1790 | self.async_prefix + "with {}:", ", ".join(args) 1791 | ) 1792 | self.suite.display(indent + 1) 1793 | 1794 | 1795 | class ClassStatement(DecorableStatement): 1796 | def __init__(self, func, name, parents, kwargs): 1797 | DecorableStatement.__init__(self) 1798 | self.func = func 1799 | self.parents = parents 1800 | self.kwargs = kwargs 1801 | 1802 | def store(self, dec, dest): 1803 | self.name = dest 1804 | dec.suite.add_statement(self) 1805 | 1806 | def display_undecorated(self, indent): 1807 | if self.parents or self.kwargs: 1808 | args = [str(x) for x in self.parents] 1809 | kwargs = [ 1810 | "{}={}".format(str(k).replace('\'', ''), v) 1811 | for k, v in self.kwargs 1812 | ] 1813 | all_args = ", ".join(args + kwargs) 1814 | indent.write("class {}({}):", self.name, all_args) 1815 | else: 1816 | indent.write("class {}:", self.name) 1817 | suite = self.func.code.get_suite(look_for_docstring=True) 1818 | if suite: 1819 | # TODO: find out why sometimes the class suite ends with 1820 | # "return __class__" 1821 | last_stmt = suite[-1] 1822 | if isinstance(last_stmt, SimpleStatement) and last_stmt.val.startswith( 1823 | "return " 1824 | ): 1825 | suite.statements.pop() 1826 | clean_vars = ['__module__', '__qualname__'] 1827 | for clean_var in clean_vars: 1828 | for i in range(len(suite.statements)): 1829 | stmt = suite.statements[i] 1830 | if isinstance(stmt, AssignStatement) and str( 1831 | stmt 1832 | ).startswith(clean_var): 1833 | suite.statements.pop(i) 1834 | break 1835 | 1836 | suite.display(indent + 1) 1837 | 1838 | 1839 | class Suite: 1840 | def __init__(self): 1841 | self.statements = [] 1842 | 1843 | def __bool__(self) -> bool: 1844 | return bool(self.statements) 1845 | 1846 | def __len__(self) -> int: 1847 | return len(self.statements) 1848 | 1849 | def __getitem__(self, i) -> PyStatement: 1850 | return self.statements[i] 1851 | 1852 | def __setitem__(self, i, val: PyStatement): 1853 | self.statements[i] = val 1854 | 1855 | def __str__(self): 1856 | istr = IndentString() 1857 | self.display(istr) 1858 | return str(istr) 1859 | 1860 | def display(self, indent): 1861 | if self.statements: 1862 | for stmt in self.statements: 1863 | stmt.display(indent) 1864 | else: 1865 | indent.write("pass") 1866 | 1867 | def gen_display(self, seq=()): 1868 | if len(self) != 1: 1869 | raise Exception( 1870 | 'There should only be one statement in a generator.' 1871 | ) 1872 | return self[0].gen_display(seq) 1873 | 1874 | def add_statement(self, stmt): 1875 | self.statements.append(stmt) 1876 | 1877 | 1878 | class SuiteDecompiler: 1879 | # An instruction handler can return this to indicate to the run() 1880 | # function that it should return immediately 1881 | END_NOW = object() 1882 | 1883 | # This is put on the stack by LOAD_BUILD_CLASS 1884 | BUILD_CLASS = object() 1885 | 1886 | def __init__(self, start_addr, end_addr=None, stack=None): 1887 | self.start_addr = start_addr 1888 | self.end_addr = end_addr 1889 | self.code: Code = start_addr.code 1890 | self.stack = Stack() if stack is None else stack 1891 | self.suite: Suite = Suite() 1892 | self.assignment_chain = [] 1893 | self.popjump_stack = [] 1894 | self.is_loop = False 1895 | self.convert_return_break = False 1896 | other_self = sys._getframe(1).f_locals.get("self") 1897 | if other_self and hasattr(other_self, "is_loop"): 1898 | self.is_loop = other_self.is_loop 1899 | 1900 | def IS_OP(self, addr, oparg): 1901 | # case TARGET(IS_OP): 1902 | right = self.stack.pop() 1903 | left = self.stack.peek() # was TOP() 1904 | # res: int = (1 if left == right else 0) ^ oparg; 1905 | # b : bool = True if res else False; 1906 | self.stack.push(PyIsOp(left, right, oparg)) 1907 | # self.stack.push(b); 1908 | 1909 | def CONTAINS_OP(self, addr, oparg): 1910 | right = self.stack.pop() 1911 | left = self.stack.pop() 1912 | pyseq = right 1913 | item = left 1914 | # res: int = 1 if (left in pyseq) else 0; 1915 | # if res < 0: raise AssertionError("res < 0: %d" % res) 1916 | # b: bool = true if (res ^ oparg) else false 1917 | # self.stack.push(b) 1918 | self.stack.push(PyInOp(left, right, oparg)) 1919 | 1920 | def push_popjump(self, jtruthiness, jaddr, jcond, original_jaddr): 1921 | stack = self.popjump_stack 1922 | if jaddr and jaddr[-1].is_else_jump: 1923 | # Increase jaddr to the 'else' address if it jumps to the 'then' 1924 | jaddr = jaddr[-1].jump() 1925 | while stack: 1926 | truthiness, addr, cond, original_addr = stack[-1] 1927 | # if jaddr == None: 1928 | # raise Exception("#ERROR: jaddr is None") 1929 | # jaddr == None \ 1930 | if jaddr and jaddr < addr or jaddr == addr: 1931 | break 1932 | stack.pop() 1933 | obj_maker = PyBooleanOr if truthiness else PyBooleanAnd 1934 | if truthiness and jtruthiness: 1935 | if original_jaddr.arg == original_addr.arg: 1936 | obj_maker = PyBooleanAnd 1937 | cond = PyNot(cond) 1938 | jcond = PyNot(jcond) 1939 | elif original_jaddr.arg > original_addr.arg: 1940 | obj_maker = PyBooleanOr 1941 | jcond = PyNot(jcond) 1942 | if ( 1943 | not truthiness 1944 | and not jtruthiness 1945 | and ( 1946 | original_jaddr.arg < original_addr.arg 1947 | or original_jaddr.arg > original_addr.arg 1948 | ) 1949 | ): 1950 | obj_maker = PyBooleanOr 1951 | cond = PyNot(cond) 1952 | if ( 1953 | truthiness 1954 | and not jtruthiness 1955 | and original_jaddr.arg == original_addr.arg 1956 | ): 1957 | obj_maker = PyBooleanAnd 1958 | cond = PyNot(cond) 1959 | if isinstance(jcond, obj_maker): 1960 | # Use associativity of 'and' and 'or' to minimise the 1961 | # number of parentheses 1962 | jcond = obj_maker( 1963 | obj_maker(cond, jcond.left), jcond.right 1964 | ) 1965 | else: 1966 | jcond = obj_maker(cond, jcond) 1967 | stack.append((jtruthiness, jaddr, jcond, original_jaddr)) 1968 | 1969 | def pop_popjump(self): 1970 | if not self.popjump_stack: 1971 | raise Exception( 1972 | 'Attempted to pop an empty popjump stack.' 1973 | ) 1974 | ( 1975 | truthiness, 1976 | addr, 1977 | cond, 1978 | original_addr, 1979 | ) = self.popjump_stack.pop() 1980 | return cond 1981 | 1982 | def run(self): 1983 | addr, end_addr = self.start_addr, self.end_addr 1984 | while addr and addr < end_addr: 1985 | opcode, arg = addr 1986 | args = (addr,) if opcode < HAVE_ARGUMENT else (addr, arg) 1987 | try: 1988 | method = getattr(self, opname[opcode]) 1989 | new_addr = method(*args) 1990 | if new_addr is self.END_NOW: 1991 | break 1992 | elif new_addr is None: 1993 | new_addr = addr[1] 1994 | addr = new_addr 1995 | except BaseException as e: 1996 | traceback.print_exception(BaseException, e, e.__traceback__) 1997 | addr = None 1998 | return addr 1999 | 2000 | def write(self, template, *args): 2001 | def fmt(x): 2002 | return self.stack.getval(x) if isinstance(x, int) else x 2003 | 2004 | line = template.format(*map(fmt, args)) if args else template 2005 | self.suite.add_statement(SimpleStatement(line)) 2006 | 2007 | def store(self, dest): 2008 | val = self.stack.pop() 2009 | val.store(self, dest) 2010 | 2011 | def is_for_loop(self, addr, end_addr): 2012 | i = 0 2013 | while 1: 2014 | cur_addr = addr[i] 2015 | if cur_addr == end_addr: 2016 | break 2017 | elif cur_addr.opcode in else_jump_opcodes: 2018 | cur_addr = cur_addr.jump() 2019 | if cur_addr and cur_addr.opcode in for_jump_opcodes: 2020 | return True 2021 | break 2022 | elif cur_addr.opcode in for_jump_opcodes: 2023 | return True 2024 | i = i + 1 2025 | return False 2026 | 2027 | def scan_to_first_jump_if( 2028 | self, addr: Address, end_addr: Address 2029 | ) -> Union[Address, None]: 2030 | i = 0 2031 | while 1: 2032 | cur_addr = addr[i] 2033 | if cur_addr == end_addr: 2034 | break 2035 | elif cur_addr.opcode in pop_jump_if_opcodes: 2036 | return cur_addr 2037 | elif cur_addr.opcode in else_jump_opcodes: 2038 | break 2039 | elif cur_addr.opcode in for_jump_opcodes: 2040 | break 2041 | i = i + 1 2042 | return None 2043 | 2044 | def scan_for_final_jump(self, start_addr, end_addr): 2045 | i = 0 2046 | end = None 2047 | while 1: 2048 | cur_addr = end_addr[i] 2049 | if cur_addr == start_addr: 2050 | break 2051 | elif cur_addr.opcode is JUMP_ABSOLUTE: 2052 | end = cur_addr 2053 | return end 2054 | elif cur_addr.opcode in else_jump_opcodes: 2055 | break 2056 | elif cur_addr.opcode in pop_jump_if_opcodes: 2057 | break 2058 | i = i - 1 2059 | return end 2060 | 2061 | # 2062 | # All opcode methods in CAPS below. 2063 | # 2064 | 2065 | 2066 | 2067 | def SETUP_LOOP(self, addr: Address, delta): 2068 | jump_addr = addr.jump() 2069 | end_addr = jump_addr[-1] 2070 | 2071 | end_cond = self.scan_to_first_jump_if(addr[1], end_addr) 2072 | if ( 2073 | end_addr.opcode in (POP_BLOCK, POP_TOP) 2074 | or not end_cond 2075 | or end_addr.seek_back( 2076 | else_jump_opcodes, end_addr.seek_back(stmt_opcodes) 2077 | ) 2078 | ): # assume conditional 2079 | # scan to first jump 2080 | end_jump = end_cond.jump() if end_cond else None 2081 | if end_jump and end_jump.opcode is POP_BLOCK: 2082 | end_jump = end_jump[1] 2083 | 2084 | if end_cond and end_cond[1].opcode is BREAK_LOOP: 2085 | end_cond = None 2086 | if end_cond and end_jump == jump_addr: 2087 | # scan for conditional 2088 | d_cond = SuiteDecompiler(addr[1], end_cond) 2089 | # 2090 | d_cond.run() 2091 | cond = d_cond.stack.pop() 2092 | if end_cond.opcode is POP_JUMP_IF_TRUE: 2093 | cond = PyNot(cond) 2094 | d_body = SuiteDecompiler(end_cond[1], end_addr) 2095 | while_stmt = WhileStatement(cond, d_body.suite) 2096 | d_body.stack.push(while_stmt) 2097 | d_body.run() 2098 | while_stmt.body = d_body.suite 2099 | self.suite.add_statement(while_stmt) 2100 | return jump_addr 2101 | elif ( 2102 | not end_cond or end_cond.jump()[1] != addr.jump() 2103 | ) and not self.is_for_loop(addr[1], end_addr): 2104 | d_body = SuiteDecompiler(addr[1], end_addr) 2105 | while_stmt = WhileStatement( 2106 | PyConst(True), d_body.suite 2107 | ) 2108 | d_body.stack.push(while_stmt) 2109 | d_body.run() 2110 | while_stmt.body = d_body.suite 2111 | self.suite.add_statement(while_stmt) 2112 | return jump_addr 2113 | return None 2114 | 2115 | def BREAK_LOOP(self, addr): 2116 | self.write("break") 2117 | 2118 | def CONTINUE_LOOP(self, addr, *argv): 2119 | self.write("continue") 2120 | 2121 | def SETUP_FINALLY(self, addr, delta): 2122 | start_finally: Address = addr.jump() 2123 | d_try = SuiteDecompiler(addr[1], start_finally) 2124 | d_try.run() 2125 | d_finally = SuiteDecompiler(start_finally) 2126 | end_finally = d_finally.run() 2127 | self.suite.add_statement( 2128 | FinallyStatement(d_try.suite, d_finally.suite) 2129 | ) 2130 | return end_finally[1] if end_finally else self.END_NOW 2131 | 2132 | def END_FINALLY(self, addr): 2133 | return self.END_NOW 2134 | 2135 | def SETUP_EXCEPT(self, addr, delta): 2136 | end_addr = addr 2137 | start_except = addr.jump() 2138 | start_try = addr[1] 2139 | end_try = start_except 2140 | if sys.version_info < (3, 7): 2141 | if end_try.opcode is JUMP_FORWARD: 2142 | end_try = end_try[1] + end_try.arg 2143 | elif end_try.opcode is JUMP_ABSOLUTE: 2144 | end_try = end_try[-1] 2145 | else: 2146 | end_try = end_try[1] 2147 | d_try = SuiteDecompiler(start_try, end_try) 2148 | d_try.run() 2149 | 2150 | stmt = TryStatement(d_try.suite) 2151 | j_except: Address = None 2152 | while start_except.opcode is not END_FINALLY: 2153 | if start_except.opcode is DUP_TOP: 2154 | # There's a new except clause 2155 | d_except = SuiteDecompiler(start_except[1]) 2156 | d_except.stack.push(stmt) 2157 | d_except.run() 2158 | start_except = stmt.next_start_except 2159 | j_except = start_except[-1] 2160 | end_addr = start_except[1] 2161 | elif start_except.opcode is POP_TOP: 2162 | # It's a bare except clause - it starts: 2163 | # POP_TOP 2164 | # POP_TOP 2165 | # POP_TOP 2166 | # 2167 | # POP_EXCEPT 2168 | start_except = start_except[3] 2169 | end_except = start_except 2170 | 2171 | nested_try: int = 0 2172 | while ( 2173 | end_except 2174 | and end_except[-1].opcode is not RETURN_VALUE 2175 | ): 2176 | if end_except.opcode is SETUP_EXCEPT: 2177 | nested_try += 1 2178 | if end_except.opcode is POP_EXCEPT: 2179 | if nested_try == 0: 2180 | break 2181 | nested_try -= 1 2182 | end_except = end_except[1] 2183 | # Handle edge case where there is a return in the except 2184 | if end_except[-1].opcode is RETURN_VALUE: 2185 | d_except = SuiteDecompiler( 2186 | start_except, end_except 2187 | ) 2188 | end_except = d_except.run() 2189 | stmt.add_except_clause(None, d_except.suite) 2190 | self.suite.add_statement(stmt) 2191 | return end_except 2192 | 2193 | d_except = SuiteDecompiler(start_except, end_except) 2194 | end_except = d_except.run() 2195 | stmt.add_except_clause(None, d_except.suite) 2196 | start_except = end_except[2] 2197 | assert start_except.opcode is END_FINALLY 2198 | 2199 | end_addr = start_except[1] 2200 | j_except: Address = end_except[1] 2201 | self.suite.add_statement(stmt) 2202 | if j_except and j_except.opcode in ( 2203 | JUMP_FORWARD, 2204 | JUMP_ABSOLUTE, 2205 | RETURN_VALUE, 2206 | ): 2207 | j_next = j_except.jump() 2208 | start_else = end_addr 2209 | if j_next: 2210 | if j_next < start_else: 2211 | if j_next < start_else and "SETUP_LOOP" in globals(): 2212 | j_next = j_next.seek_back(SETUP_LOOP) 2213 | if j_next: 2214 | j_next = j_next.jump() 2215 | else: 2216 | return_count = 0 2217 | next_return = start_else 2218 | while next_return: 2219 | if next_return.opcode in pop_jump_if_opcodes: 2220 | j_next_return = next_return.jump() 2221 | if j_next_return > next_return: 2222 | next_return = j_next_return 2223 | if next_return.opcode is RETURN_VALUE: 2224 | return_count += 1 2225 | next_return = next_return[1] 2226 | if return_count == 1: 2227 | return end_addr 2228 | 2229 | end_else = j_next 2230 | d_else = SuiteDecompiler(start_else, end_else) 2231 | end_addr = d_else.run() 2232 | if not end_addr: 2233 | end_addr = self.END_NOW 2234 | stmt.else_suite = d_else.suite 2235 | return end_addr 2236 | 2237 | def SETUP_WITH(self, addr, delta): 2238 | end_with = addr.jump() 2239 | with_stmt = WithStatement(self.stack.pop()) 2240 | d_with = SuiteDecompiler(addr[1], end_with) 2241 | d_with.stack.push(with_stmt) 2242 | d_with.run() 2243 | with_stmt.suite = d_with.suite 2244 | self.suite.add_statement(with_stmt) 2245 | if sys.version_info <= (3, 4): 2246 | assert end_with.opcode is WITH_CLEANUP 2247 | assert end_with[1].opcode is END_FINALLY 2248 | return end_with[2] 2249 | elif end_with.opcode is WITH_CLEANUP_START: 2250 | assert end_with.opcode is WITH_CLEANUP_START 2251 | assert end_with[1].opcode is WITH_CLEANUP_FINISH 2252 | return end_with[3] 2253 | elif end_with.opcode is WITH_EXCEPT_START: 2254 | """ 2255 | TARGET(WITH_EXCEPT_START) { 2256 | /* At the top of the stack are 4 values: 2257 | - TOP = exc_info() 2258 | - SECOND = previous exception 2259 | - THIRD: lasti of exception in exc_info() 2260 | - FOURTH: the context.__exit__ bound method 2261 | We call FOURTH(type(TOP), TOP, GetTraceback(TOP)). 2262 | Then we push the __exit__ return value. 2263 | */ 2264 | PyObject *exit_func; 2265 | PyObject *exc, *val, *tb, *res; 2266 | val = TOP(); 2267 | assert(val && PyExceptionInstance_Check(val)); 2268 | exc = PyExceptionInstance_Class(val); 2269 | tb = PyException_GetTraceback(val); 2270 | Py_XDECREF(tb); 2271 | assert(PyLong_Check(PEEK(3))); 2272 | exit_func = PEEK(4); 2273 | PyObject *stack[4] = {NULL, exc, val, tb}; 2274 | res = PyObject_Vectorcall(exit_func, stack + 1, 2275 | 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); 2276 | } 2277 | """ 2278 | start_index = [ 2279 | idx 2280 | for idx, (a, (k, v)) in enumerate( 2281 | end_with.code.instr_seq 2282 | ) 2283 | if a == end_with[0].addr 2284 | ][0] 2285 | end_index = [ 2286 | idx 2287 | for idx, (a, (k, v)) in enumerate( 2288 | end_with.code.instr_seq 2289 | ) 2290 | if idx > start_index and k == POP_EXCEPT 2291 | ][0] 2292 | """ 2293 | >> 182 WITH_EXCEPT_START 2294 | 184 POP_JUMP_IF_TRUE 188 2295 | 186 RERAISE 2296 | >> 188 POP_TOP 2297 | 190 POP_TOP 2298 | 192 POP_TOP 2299 | 194 POP_EXCEPT 2300 | """ 2301 | assert end_with.opcode is WITH_EXCEPT_START 2302 | assert ( 2303 | end_with[end_index - start_index].opcode is POP_EXCEPT 2304 | ) 2305 | return end_with[end_index - start_index] 2306 | else: 2307 | raise AssertionError( 2308 | "Unexpectee opcode at start of BEGIN_WITH:" 2309 | " end_with.opcode = {}".format(end_with.opcode) 2310 | ) 2311 | 2312 | def POP_BLOCK(self, addr): 2313 | pass 2314 | 2315 | def POP_EXCEPT(self, addr): 2316 | return self.END_NOW 2317 | 2318 | def NOP(self, addr): 2319 | return 2320 | 2321 | def SETUP_ANNOTATIONS(self, addr): 2322 | return 2323 | 2324 | def COMPARE_OP(self, addr, compare_opname): 2325 | left, right = self.stack.pop(2) 2326 | if compare_opname != 10: # 10 is exception match 2327 | self.stack.push( 2328 | PyCompare([left, cmp_op[compare_opname], right]) 2329 | ) 2330 | else: 2331 | # It's an exception match 2332 | # left is a TryStatement 2333 | # right is the exception type to be matched 2334 | # It goes: 2335 | # COMPARE_OP 10 2336 | # POP_JUMP_IF_FALSE 2337 | # POP_TOP 2338 | # POP_TOP or STORE_FAST (if the match is named) 2339 | # POP_TOP 2340 | # SETUP_FINALLY if the match was named 2341 | assert addr[1].opcode is POP_JUMP_IF_FALSE 2342 | left.next_start_except = addr[1].jump() 2343 | assert addr[2].opcode is POP_TOP 2344 | assert addr[4].opcode is POP_TOP 2345 | if addr[5].opcode is SETUP_FINALLY: 2346 | except_start = addr[6] 2347 | except_end = addr[5].jump() 2348 | else: 2349 | except_start = addr[5] 2350 | except_end = left.next_start_except 2351 | d_body = SuiteDecompiler(except_start, except_end) 2352 | d_body.run() 2353 | left.add_except_clause(right, d_body.suite) 2354 | if addr[3].opcode is not POP_TOP: 2355 | # The exception is named 2356 | d_exc_name = SuiteDecompiler(addr[3], addr[4]) 2357 | d_exc_name.stack.push(left) 2358 | # This will store the name in left: 2359 | d_exc_name.run() 2360 | # We're done with this except clause 2361 | return self.END_NOW 2362 | 2363 | # 2364 | # Stack manipulation 2365 | # 2366 | 2367 | def POP_TOP(self, addr): 2368 | val = self.stack.pop() 2369 | if (sys.version_info > (3, 7) and not self.code.is_comp 2370 | and isinstance(val, (ForStatement, WhileStatement)) 2371 | and addr[1].opcode is JUMP_ABSOLUTE): 2372 | self.write("break") 2373 | return addr[2] 2374 | val.on_pop(self) 2375 | 2376 | calls = [] 2377 | 2378 | def PRINT_EXPR(self, addr): 2379 | 2380 | expr = self.stack.pop() 2381 | self.calls += [(self, addr, expr)] 2382 | 2383 | self.write("{}", expr) 2384 | # expr.on_pop(self) 2385 | 2386 | def ROT_TWO(self, addr): 2387 | # special case: x, y = z, t 2388 | if ( 2389 | addr[2] 2390 | and addr[1].opcode is STORE_NAME 2391 | and addr[2].opcode is STORE_NAME 2392 | ): 2393 | val = PyTuple(self.stack.pop(2)) 2394 | unpack = Unpack(val, 2) 2395 | self.stack.push(unpack) 2396 | self.stack.push(unpack) 2397 | else: 2398 | tos1, tos = self.stack.pop(2) 2399 | self.stack.push(tos, tos1) 2400 | 2401 | def ROT_THREE(self, addr): 2402 | # special case: x, y, z = a, b, c 2403 | if ( 2404 | addr[4] 2405 | and addr[1].opcode is ROT_TWO 2406 | and addr[2].opcode is STORE_NAME 2407 | and addr[3].opcode is STORE_NAME 2408 | and addr[4].opcode is STORE_NAME 2409 | ): 2410 | val = PyTuple(self.stack.pop(3)) 2411 | unpack = Unpack(val, 3) 2412 | self.stack.push(unpack) 2413 | self.stack.push(unpack) 2414 | self.stack.push(unpack) 2415 | return addr[2] 2416 | else: 2417 | tos2, tos1, tos = self.stack.pop(3) 2418 | self.stack.push(tos, tos2, tos1) 2419 | 2420 | def DUP_TOP(self, addr): 2421 | self.stack.push(self.stack.peek()) 2422 | 2423 | def DUP_TOP_TWO(self, addr): 2424 | self.stack.push(*self.stack.peek(2)) 2425 | 2426 | # 2427 | # LOAD / STORE / DELETE 2428 | # 2429 | 2430 | # FAST 2431 | 2432 | def LOAD_FAST(self, addr, var_num): 2433 | name = self.code.varnames[var_num] 2434 | self.stack.push(name) 2435 | 2436 | def STORE_FAST(self, addr, var_num): 2437 | name = self.code.varnames[var_num] 2438 | self.store(name) 2439 | 2440 | def DELETE_FAST(self, addr, var_num): 2441 | name = self.code.varnames[var_num] 2442 | self.write("del {}", name) 2443 | 2444 | # DEREF 2445 | 2446 | def LOAD_DEREF(self, addr, i): 2447 | name = self.code.derefnames[i] 2448 | self.stack.push(name) 2449 | 2450 | def LOAD_CLASSDEREF(self, addr, i): 2451 | name = self.code.derefnames[i] 2452 | self.stack.push(name) 2453 | 2454 | def STORE_DEREF(self, addr, i): 2455 | name = self.code.derefnames[i] 2456 | if not self.code.iscellvar(i): 2457 | self.code.declare_nonlocal(name) 2458 | self.store(name) 2459 | 2460 | def DELETE_DEREF(self, addr, i): 2461 | name = self.code.derefnames[i] 2462 | if not self.code.iscellvar(i): 2463 | self.code.declare_nonlocal(name) 2464 | self.write("del {}", name) 2465 | 2466 | # GLOBAL 2467 | 2468 | def LOAD_GLOBAL(self, addr, namei): 2469 | name = self.code.names[namei] 2470 | self.code.ensure_global(name) 2471 | self.stack.push(name) 2472 | 2473 | def STORE_GLOBAL(self, addr, namei): 2474 | name = self.code.names[namei] 2475 | self.code.declare_global(name) 2476 | self.store(name) 2477 | 2478 | def DELETE_GLOBAL(self, addr, namei): 2479 | name = self.code.names[namei] 2480 | self.declare_global(name) 2481 | self.write("del {}", name) 2482 | 2483 | # NAME 2484 | 2485 | def LOAD_NAME(self, addr, namei): 2486 | name = self.code.names[namei] 2487 | self.stack.push(name) 2488 | 2489 | def STORE_NAME(self, addr, namei): 2490 | name = self.code.names[namei] 2491 | self.store(name) 2492 | 2493 | def DELETE_NAME(self, addr, namei): 2494 | name = self.code.names[namei] 2495 | self.write("del {}", name) 2496 | 2497 | # METHOD 2498 | def LOAD_METHOD(self, addr, namei): 2499 | expr = self.stack.pop() 2500 | attrname = self.code.names[namei] 2501 | self.stack.push(PyAttribute(expr, attrname)) 2502 | 2503 | def CALL_METHOD(self, addr, argc, have_var=False, have_kw=False): 2504 | kw_argc = argc >> 8 2505 | pos_argc = argc 2506 | varkw = self.stack.pop() if have_kw else None 2507 | varargs = self.stack.pop() if have_var else None 2508 | kwargs_iter = iter(self.stack.pop(2 * kw_argc)) 2509 | kwargs = list(zip(kwargs_iter, kwargs_iter)) 2510 | posargs = self.stack.pop(pos_argc) 2511 | func = self.stack.pop() 2512 | if func is self.BUILD_CLASS: 2513 | # It's a class construction 2514 | # TODO: check the assert statement below is correct 2515 | assert not (have_var or have_kw) 2516 | func, name, *parents = posargs 2517 | self.stack.push( 2518 | ClassStatement(func, name, parents, kwargs) 2519 | ) 2520 | elif isinstance(func, PyComp): 2521 | # It's a list/set/dict comprehension or generator expression 2522 | assert not (have_var or have_kw) 2523 | assert len(posargs) == 1 and not kwargs 2524 | func.set_iterable(posargs[0]) 2525 | self.stack.push(func) 2526 | elif posargs and isinstance(posargs[0], DecorableStatement): 2527 | # It's a decorator for a def/class statement 2528 | assert len(posargs) == 1 and not kwargs 2529 | defn = posargs[0] 2530 | defn.decorate(func) 2531 | self.stack.push(defn) 2532 | else: 2533 | # It's none of the above, so it must be a normal function call 2534 | func_call = PyCallFunction( 2535 | func, posargs, kwargs, varargs, varkw 2536 | ) 2537 | self.stack.push(func_call) 2538 | 2539 | # ATTR 2540 | 2541 | def LOAD_ATTR(self, addr, namei): 2542 | expr = self.stack.pop() 2543 | attrname = self.code.names[namei] 2544 | self.stack.push(PyAttribute(expr, attrname)) 2545 | 2546 | def STORE_ATTR(self, addr, namei): 2547 | expr = self.stack.pop1() 2548 | attrname = self.code.names[namei] 2549 | self.store(PyAttribute(expr, attrname)) 2550 | 2551 | def DELETE_ATTR(self, addr, namei): 2552 | expr = self.stack.pop() 2553 | attrname = self.code.names[namei] 2554 | self.write("del {}.{}", expr, attrname) 2555 | 2556 | # SUBSCR 2557 | 2558 | def STORE_SUBSCR(self, addr): 2559 | expr, sub = self.stack.pop(2) 2560 | self.store(PySubscript(expr, sub)) 2561 | 2562 | def DELETE_SUBSCR(self, addr): 2563 | expr, sub = self.stack.pop(2) 2564 | self.write("del {}[{}]", expr, sub) 2565 | 2566 | # CONST 2567 | CONST_LITERALS = {Ellipsis: PyName('...')} 2568 | 2569 | def LOAD_CONST(self, addr, consti): 2570 | const = self.code.consts[consti] 2571 | if const.val in self.CONST_LITERALS: 2572 | const = self.CONST_LITERALS[const.val] 2573 | self.stack.push(const) 2574 | 2575 | # 2576 | # Import statements 2577 | # 2578 | 2579 | def IMPORT_NAME(self, addr, namei): 2580 | name = self.code.names[namei] 2581 | 2582 | if len(self.stack._stack) > 1: 2583 | level, fromlist = self.stack.pop(2) 2584 | else: 2585 | return addr[0] 2586 | self.stack.push(ImportStatement(name, level, fromlist)) 2587 | # special case check for import x.y.z as w 2588 | # syntax which uses attributes and assignments 2589 | # and is difficult to workaround 2590 | i = 1 2591 | while addr[i].opcode is LOAD_ATTR: 2592 | i = i + 1 2593 | if i > 1 and addr[i].opcode in (STORE_FAST, STORE_NAME, STORE_ATTR): 2594 | return addr[i] 2595 | return None 2596 | 2597 | def IMPORT_FROM(self, addr: Address, namei): 2598 | name = self.code.names[namei] 2599 | self.stack.push(ImportFrom(name)) 2600 | if addr[1].opcode is ROT_TWO: 2601 | return addr.seek_forward(STORE_NAME) 2602 | 2603 | def IMPORT_STAR(self, addr): 2604 | self.POP_TOP(addr) 2605 | 2606 | # 2607 | # Function call 2608 | # 2609 | 2610 | def STORE_LOCALS(self, addr): 2611 | self.stack.pop() 2612 | return addr[3] 2613 | 2614 | def LOAD_BUILD_CLASS(self, addr): 2615 | self.stack.push(self.BUILD_CLASS) 2616 | 2617 | def RETURN_VALUE(self, addr): 2618 | value = self.stack.pop() 2619 | if isinstance(value, PyConst) and value.val is None: 2620 | if addr[1] is not None: 2621 | if ( 2622 | self.code.flags.generator 2623 | and addr[3] 2624 | and not self.code[0].seek_forward( 2625 | {YIELD_FROM, YIELD_VALUE} 2626 | ) 2627 | ): 2628 | self.write('yield') 2629 | else: 2630 | self.write("return") 2631 | return 2632 | if self.code.flags.iterable_coroutine: 2633 | self.write("yield {}", value) 2634 | else: 2635 | self.write("return {}", value) 2636 | if self.code.flags.generator: 2637 | self.write('yield') 2638 | 2639 | def GET_YIELD_FROM_ITER(self, addr): 2640 | pass 2641 | 2642 | def YIELD_VALUE(self, addr): 2643 | if self.code.name == '': 2644 | return 2645 | value = self.stack.pop() 2646 | self.stack.push(PyYield(value)) 2647 | 2648 | def YIELD_FROM(self, addr): 2649 | value = self.stack.pop() # TODO: from statement ? 2650 | value = self.stack.pop() 2651 | self.stack.push(PyYieldFrom(value)) 2652 | 2653 | def CALL_FUNCTION_CORE( 2654 | self, func, posargs, kwargs, varargs, varkw 2655 | ): 2656 | if func is self.BUILD_CLASS: 2657 | # It's a class construction 2658 | # TODO: check the assert statement below is correct 2659 | # assert not (have_var or have_kw) 2660 | func, name, *parents = posargs 2661 | self.stack.push( 2662 | ClassStatement(func, name, parents, kwargs) 2663 | ) 2664 | elif isinstance(func, PyComp): 2665 | # It's a list/set/dict comprehension or generator expression 2666 | # assert not (have_var or have_kw) 2667 | assert len(posargs) == 1 and not kwargs 2668 | func.set_iterable(posargs[0]) 2669 | self.stack.push(func) 2670 | elif ( 2671 | posargs 2672 | and isinstance(posargs, list) 2673 | and isinstance(posargs[0], DecorableStatement) 2674 | ): 2675 | # It's a decorator for a def/class statement 2676 | assert len(posargs) == 1 and not kwargs 2677 | defn = posargs[0] 2678 | defn.decorate(func) 2679 | self.stack.push(defn) 2680 | else: 2681 | # It's none of the above, so it must be a normal function call 2682 | func_call = PyCallFunction( 2683 | func, posargs, kwargs, varargs, varkw 2684 | ) 2685 | self.stack.push(func_call) 2686 | 2687 | def CALL_FUNCTION( 2688 | self, addr, argc, have_var=False, have_kw=False 2689 | ): 2690 | if sys.version_info >= (3, 6): 2691 | pos_argc = argc 2692 | posargs = self.stack.pop(pos_argc) 2693 | func = self.stack.pop() 2694 | self.CALL_FUNCTION_CORE(func, posargs, [], None, None) 2695 | else: 2696 | kw_argc = argc >> 8 2697 | pos_argc = argc & 0xFF 2698 | varkw = self.stack.pop() if have_kw else None 2699 | varargs = self.stack.pop() if have_var else None 2700 | kwargs_iter = iter(self.stack.pop(2 * kw_argc)) 2701 | kwargs = list(zip(kwargs_iter, kwargs_iter)) 2702 | posargs = self.stack.pop(pos_argc) 2703 | func = self.stack.pop() 2704 | self.CALL_FUNCTION_CORE( 2705 | func, posargs, kwargs, varargs, varkw 2706 | ) 2707 | 2708 | def CALL_FUNCTION_VAR(self, addr, argc): 2709 | self.CALL_FUNCTION(addr, argc, have_var=True) 2710 | 2711 | def CALL_FUNCTION_KW(self, addr, argc): 2712 | if sys.version_info >= (3, 6): 2713 | keys = self.stack.pop() 2714 | kwargc = len(keys.val) 2715 | kwarg_values = self.stack.pop(kwargc) 2716 | posargs = self.stack.pop(argc - kwargc) 2717 | func = self.stack.pop() 2718 | kwarg_dict = list( 2719 | zip([PyName(k) for k in keys], kwarg_values) 2720 | ) 2721 | self.CALL_FUNCTION_CORE( 2722 | func, posargs, kwarg_dict, None, None 2723 | ) 2724 | else: 2725 | self.CALL_FUNCTION(addr, argc, have_kw=True) 2726 | 2727 | def CALL_FUNCTION_EX(self, addr, flags): 2728 | kwarg_unpacks = [] 2729 | if flags & 1: 2730 | kwarg_unpacks = self.stack.pop() 2731 | 2732 | kwarg_dict = PyDict() 2733 | if isinstance(kwarg_unpacks, PyDict): 2734 | kwarg_dict = kwarg_unpacks 2735 | kwarg_unpacks = [] 2736 | elif isinstance(kwarg_unpacks, list): 2737 | if len(kwarg_unpacks): 2738 | if isinstance(kwarg_unpacks[0], PyDict): 2739 | kwarg_dict = kwarg_unpacks[0] 2740 | kwarg_unpacks = kwarg_unpacks[1:] 2741 | else: 2742 | kwarg_unpacks = [kwarg_unpacks] 2743 | 2744 | if any( 2745 | filter(lambda kv: '.' in str(kv[0]), kwarg_dict.items) 2746 | ): 2747 | kwarg_unpacks.append(kwarg_dict) 2748 | kwarg_dict = PyDict() 2749 | 2750 | posargs_unpacks = self.stack.pop() 2751 | posargs = PyTuple([]) 2752 | if isinstance(posargs_unpacks, PyTuple): 2753 | posargs = posargs_unpacks 2754 | posargs_unpacks = [] 2755 | elif isinstance(posargs_unpacks, list): 2756 | if len(posargs_unpacks) > 0: 2757 | posargs = posargs_unpacks[0] 2758 | if isinstance(posargs, PyConst): 2759 | posargs = PyTuple( 2760 | [PyConst(a) for a in posargs.val] 2761 | ) 2762 | elif isinstance(posargs, PyAttribute): 2763 | posargs = PyTuple([posargs]) 2764 | posargs_unpacks = posargs_unpacks[1:] 2765 | else: 2766 | posargs_unpacks = [posargs_unpacks] 2767 | 2768 | func = self.stack.pop() 2769 | self.CALL_FUNCTION_CORE( 2770 | func, 2771 | list(posargs.values), 2772 | list(kwarg_dict.items), 2773 | posargs_unpacks, 2774 | kwarg_unpacks, 2775 | ) 2776 | 2777 | def CALL_FUNCTION_VAR_KW(self, addr, argc): 2778 | self.CALL_FUNCTION(addr, argc, have_var=True, have_kw=True) 2779 | 2780 | # a, b, ... = ... 2781 | 2782 | def UNPACK_SEQUENCE(self, addr, count): 2783 | unpack = Unpack(self.stack.pop(), count) 2784 | for i in range(count): 2785 | self.stack.push(unpack) 2786 | 2787 | def UNPACK_EX(self, addr, counts): 2788 | rcount = counts >> 8 2789 | lcount = counts & 0xFF 2790 | count = lcount + rcount + 1 2791 | unpack = Unpack(self.stack.pop(), count, lcount) 2792 | for i in range(count): 2793 | self.stack.push(unpack) 2794 | 2795 | # Build operations 2796 | 2797 | def BUILD_SLICE(self, addr, argc): 2798 | assert argc in (2, 3) 2799 | self.stack.push(PySlice(self.stack.pop(argc))) 2800 | 2801 | def BUILD_TUPLE(self, addr, count): 2802 | values = [self.stack.pop() for i in range(count)] 2803 | values.reverse() 2804 | self.stack.push(PyTuple(values)) 2805 | 2806 | def DICT_MERGE(self, addr, count): 2807 | values = [] 2808 | for o in self.stack.pop(count): 2809 | if isinstance(o, PyTuple): 2810 | values.extend(o.values) 2811 | else: 2812 | values.append(PyStarred(o)) 2813 | self.stack.push(PyList(values)) 2814 | 2815 | def BUILD_TUPLE_UNPACK(self, addr, count): 2816 | values = [] 2817 | for o in self.stack.pop(count): 2818 | if isinstance(o, PyTuple): 2819 | values.extend(o.values) 2820 | else: 2821 | values.append(PyStarred(o)) 2822 | 2823 | self.stack.push(PyTuple(values)) 2824 | 2825 | def BUILD_TUPLE_UNPACK_WITH_CALL(self, addr, count): 2826 | self.stack.push(self.stack.pop(count)) 2827 | 2828 | def BUILD_LIST(self, addr, count): 2829 | values = [self.stack.pop() for i in range(count)] 2830 | values.reverse() 2831 | self.stack.push(PyList(values)) 2832 | 2833 | def BUILD_LIST_UNPACK(self, addr, count): 2834 | values = [] 2835 | for o in self.stack.pop(count): 2836 | if isinstance(o, PyTuple): 2837 | values.extend(o.values) 2838 | else: 2839 | values.append(PyStarred(o)) 2840 | 2841 | self.stack.push(PyList(values)) 2842 | 2843 | def BUILD_SET(self, addr, count): 2844 | values = [self.stack.pop() for i in range(count)] 2845 | values.reverse() 2846 | self.stack.push(PySet(values)) 2847 | 2848 | def BUILD_SET_UNPACK(self, addr, count): 2849 | values = [] 2850 | for o in self.stack.pop(count): 2851 | if isinstance(o, PySet): 2852 | values.extend(o.values) 2853 | else: 2854 | values.append(PyStarred(o)) 2855 | 2856 | self.stack.push(PySet(values)) 2857 | 2858 | def BUILD_MAP(self, addr, count): 2859 | d = PyDict() 2860 | if sys.version_info >= (3, 5): 2861 | for i in range(count): 2862 | d.items.append(tuple(self.stack.pop(2))) 2863 | d.items = list(reversed(d.items)) 2864 | self.stack.push(d) 2865 | 2866 | def BUILD_MAP_UNPACK(self, addr, count): 2867 | d = PyDict() 2868 | for i in range(count): 2869 | o = self.stack.pop() 2870 | if isinstance(o, PyDict): 2871 | for item in reversed(o.items): 2872 | k, v = item 2873 | d.set_item( 2874 | PyConst( 2875 | k.val 2876 | if isinstance(k, PyConst) 2877 | else k.name 2878 | ), 2879 | v, 2880 | ) 2881 | else: 2882 | d.items.append((PyStarred(PyStarred(o)),)) 2883 | d.items = list(reversed(d.items)) 2884 | self.stack.push(d) 2885 | 2886 | def BUILD_MAP_UNPACK_WITH_CALL(self, addr, count): 2887 | self.stack.push(self.stack.pop(count)) 2888 | 2889 | def BUILD_CONST_KEY_MAP(self, addr, count): 2890 | keys = self.stack.pop() 2891 | vals = self.stack.pop(count) 2892 | dict = PyDict() 2893 | for i in range(count): 2894 | dict.set_item(PyConst(keys.val[i]), vals[i]) 2895 | self.stack.push(dict) 2896 | 2897 | def STORE_MAP(self, addr): 2898 | v, k = self.stack.pop(2) 2899 | d = self.stack.peek() 2900 | d.set_item(k, v) 2901 | 2902 | # Comprehension operations - just create an expression statement 2903 | 2904 | def LIST_APPEND(self, addr, i): 2905 | self.POP_TOP(addr) 2906 | 2907 | def SET_ADD(self, addr, i): 2908 | self.POP_TOP(addr) 2909 | 2910 | def MAP_ADD(self, addr, i): 2911 | value, key = self.stack.pop(2) 2912 | self.stack.push(PyKeyValue(key, value)) 2913 | self.POP_TOP(addr) 2914 | 2915 | """ 2916 | def LIST_TO_TUPLE(self, addr): 2917 | list_obj = self.stack.pop() 2918 | tuple_obj = PyTuple(list_obj) 2919 | #self.POP_TOP(addr) 2920 | self.stack.push(tuple_obj) 2921 | 2922 | def LIST_EXTEND(self, addr, oparg): 2923 | iterable = self.stack.pop() 2924 | list_obj = self.stack.peek(oparg) 2925 | none_val = None 2926 | try: 2927 | list_obj += [iterable] 2928 | #self.stack.push(PyList(list_obj)) 2929 | except TypeError as _te: 2930 | if not hasattr(iterable, "__iter__") and \ 2931 | not hasattr(iterable, "__getitem__"): 2932 | raise TypeError( 2933 | "Value after * must be an iterable, not {!s}".format( 2934 | type(iterable).__name__)) 2935 | list_obj[0].values + [PyStarred(iterable)] 2936 | self.stack.push(PyList(list_obj[0].values)) 2937 | #self.POP_TOP(addr) 2938 | 2939 | def SET_UPDATE(self, addr, i, oparg): 2940 | iterable = self.stack.pop() 2941 | set_obj = self.stack.peek(oparg)[0] 2942 | for item in iterable: 2943 | set_obj.values.add(item) 2944 | self.stack.push(set_obj) 2945 | #self.POP_TOP(addr) 2946 | 2947 | def DICT_UPDATE(self, addr, i, oparg): 2948 | update = self.stack.pop() 2949 | dict_obj = self.stack.peek(oparg)[0] 2950 | if (PyDict_Update(dict_obj, update) < 0): 2951 | if (_PyErr_ExceptionMatches(AttributeError)): 2952 | raise TypeError("'{!s}' object_obj is not a mapping".format( 2953 | type(update).__name__)) 2954 | #self.POP_TOP(addr) 2955 | self.stack.push(dict_obj) 2956 | 2957 | def DICT_MERGE(self, addr, oparg): 2958 | other = self.stack.pop() 2959 | dict_obj = self.stack.peek() 2960 | if isinstance(other, PyDict): 2961 | self.print("{}.update({})".format(dict_obj, other)) 2962 | self.stack.push(dict_obj) 2963 | else: 2964 | for key, value in dict_obj.items: 2965 | self.stack.push(PyKeyValue(key, value)) 2966 | self.stack.push(dict_obj) 2967 | #self.POP_TOP(addr) 2968 | 2969 | # and operator 2970 | """ 2971 | 2972 | def LIST_TO_TUPLE(self, addr): 2973 | list_value = self.stack.pop() 2974 | values = list_value.values 2975 | self.stack.push(PyTuple(values)) 2976 | 2977 | def LIST_EXTEND(self, addr, i): 2978 | items = self.stack.pop(1) 2979 | item2 = self.stack.pop(1) 2980 | if hasattr(item2[0], "expr"): 2981 | exprs = [item2[0].expr] 2982 | else: 2983 | exprs = item2[0].values 2984 | new_list = PyList([*exprs, PyStarred(items[0])]) 2985 | self.stack.push(new_list) 2986 | 2987 | def SET_UPDATE(self, addr, i): 2988 | self.POP_TOP(addr) 2989 | pass # self.POP_TOP(addr) 2990 | 2991 | def DICT_UPDATE(self, addr, i): 2992 | items = self.stack.pop(1) 2993 | self.stack.push(items[0]) 2994 | self.POP_TOP(addr) 2995 | 2996 | def DICT_MERGE(self, addr, i): 2997 | items = self.stack.pop(1) 2998 | item2 = self.stack.pop(1) 2999 | new_list = [*item2[0].items] + [items[0]] 3000 | item = None 3001 | if len(new_list) == 1: 3002 | item = new_list[0] 3003 | else: 3004 | item = PyList(new_list) 3005 | self.stack.push(item) 3006 | 3007 | def JUMP_IF_FALSE_OR_POP(self, addr: Address, target): 3008 | end_addr = addr.jump() 3009 | truthiness = not addr.seek_back_statement(POP_JUMP_IF_TRUE) 3010 | self.push_popjump( 3011 | truthiness, end_addr, self.stack.pop(), addr 3012 | ) 3013 | left = self.pop_popjump() 3014 | if end_addr.opcode is ROT_TWO: 3015 | opc, arg = end_addr[-1] 3016 | if opc == JUMP_FORWARD and arg == 2: 3017 | end_addr = end_addr[2] 3018 | elif opc == RETURN_VALUE or opc == JUMP_FORWARD: 3019 | end_addr = end_addr[-1] 3020 | d = SuiteDecompiler(addr[1], end_addr, self.stack) 3021 | d.run() 3022 | right = self.stack.pop() 3023 | if isinstance(right, PyCompare) and right.extends( 3024 | left 3025 | ): 3026 | py_and = left.chain(right) 3027 | else: 3028 | py_and = PyBooleanAnd(left, right) 3029 | self.stack.push(py_and) 3030 | return end_addr[3] 3031 | 3032 | d = SuiteDecompiler(addr[1], end_addr, self.stack) 3033 | if end_addr[-1].opcode is RETURN_VALUE: 3034 | d = SuiteDecompiler(addr[1], end_addr[-1], self.stack) 3035 | else: 3036 | d = SuiteDecompiler(addr[1], end_addr, self.stack) 3037 | 3038 | d.run() 3039 | # if end_addr.opcode is RETURN_VALUE: 3040 | # return end_addr[2] 3041 | right = self.stack.pop() 3042 | if isinstance(right, PyCompare) and right.extends(left): 3043 | py_and = left.chain(right) 3044 | else: 3045 | py_and = PyBooleanAnd(left, right) 3046 | self.stack.push(py_and) 3047 | return end_addr 3048 | 3049 | # This appears when there are chained comparisons, e.g. 1 <= x < 10 3050 | 3051 | def JUMP_FORWARD(self, addr, delta): 3052 | ## if delta == 2 and addr[1].opcode is ROT_TWO and addr[2].opcode is POP_TOP: 3053 | ## # We're in the special case of chained comparisons 3054 | ## return addr[3] 3055 | ## else: 3056 | ## # I'm hoping its an unused JUMP in an if-else statement 3057 | ## return addr[1] 3058 | return addr.jump() 3059 | 3060 | # or operator 3061 | 3062 | def JUMP_IF_TRUE_OR_POP(self, addr, target): 3063 | end_addr = addr.jump() 3064 | self.push_popjump(True, end_addr, self.stack.pop(), addr) 3065 | left = self.pop_popjump() 3066 | d = SuiteDecompiler(addr[1], end_addr, self.stack) 3067 | d.run() 3068 | right = self.stack.pop() 3069 | self.stack.push(PyBooleanOr(left, right)) 3070 | return end_addr 3071 | 3072 | # 3073 | # If-else statements/expressions and related structures 3074 | # 3075 | 3076 | def POP_JUMP_IF( 3077 | self, addr: Address, target: int, truthiness: bool 3078 | ) -> Union[Address, None]: 3079 | jump_addr = addr.jump() 3080 | 3081 | last_loop = None 3082 | if "SETUP_LOOP" in globals(): 3083 | last_loop = addr.seek_back(SETUP_LOOP) 3084 | 3085 | last_loop = last_loop or addr 3086 | in_loop = last_loop and last_loop.jump() > addr 3087 | end_of_loop = ( 3088 | jump_addr.opcode is FOR_ITER 3089 | or ( 3090 | "SETUP_LOOP" in globals() 3091 | and jump_addr[-1].opcode is SETUP_LOOP 3092 | ) 3093 | ) 3094 | if jump_addr.opcode is FOR_ITER: 3095 | # We are in a for-loop with nothing after the if-suite 3096 | # But take care: for-loops in generator expression do 3097 | # not end in POP_BLOCK, hence the test below. 3098 | jump_addr = jump_addr.jump() 3099 | else: 3100 | if end_of_loop: 3101 | # We are in a while-loop with nothing after the if-suite 3102 | jump_addr = jump_addr[-1].jump()[-1] 3103 | #else: 3104 | #jump_addr = addr[1] 3105 | # raise Exception("unhandled") 3106 | if self.stack._stack: 3107 | cond = self.stack.pop() 3108 | chain = self.assignment_chain 3109 | if chain: 3110 | chain = chain[::-1] 3111 | chain.append(cond) 3112 | cond = PyNamedExpr(chain) 3113 | self.assignment_chain = [] 3114 | else: 3115 | cond = not truthiness 3116 | # chained compare 3117 | # ex: 3118 | # if x <= y <= z: 3119 | if ( 3120 | addr[-3] 3121 | and addr[-1].opcode is COMPARE_OP 3122 | and addr[-2].opcode is ROT_THREE 3123 | and addr[-3].opcode is DUP_TOP 3124 | ): 3125 | if self.popjump_stack: 3126 | c = self.pop_popjump() 3127 | c = c.chain(cond) 3128 | self.push_popjump(not truthiness, jump_addr, c, addr) 3129 | else: 3130 | self.push_popjump( 3131 | not truthiness, jump_addr, cond, addr 3132 | ) 3133 | return 3134 | 3135 | is_chained = isinstance(cond, PyCompare) and addr.seek_back( 3136 | ROT_THREE, addr.seek_back(stmt_opcodes) 3137 | ) 3138 | if is_chained and self.popjump_stack: 3139 | pj = self.pop_popjump() 3140 | if isinstance(pj, PyCompare): 3141 | cond = pj.chain(cond) 3142 | 3143 | if not addr.is_else_jump: 3144 | # Handle generator expressions with or clause 3145 | for_iter = addr.seek_back(FOR_ITER) 3146 | if for_iter: 3147 | end_of_for = for_iter.jump() 3148 | if end_of_for.addr > addr.addr: 3149 | gen = jump_addr.seek_forward( 3150 | (YIELD_VALUE, LIST_APPEND), end_of_for 3151 | ) 3152 | if gen: 3153 | if not truthiness: 3154 | truthiness = not truthiness 3155 | if truthiness: 3156 | cond = PyNot(cond) 3157 | self.push_popjump( 3158 | truthiness, jump_addr, cond, addr 3159 | ) 3160 | return None 3161 | 3162 | self.push_popjump(truthiness, jump_addr, cond, addr) 3163 | # Dictionary comprehension 3164 | if jump_addr.seek_forward(MAP_ADD): 3165 | return None 3166 | 3167 | if addr.code.name == '': 3168 | return None 3169 | # Generator 3170 | if jump_addr.seek_forward(YIELD_VALUE): 3171 | return None 3172 | 3173 | if jump_addr.seek_back( 3174 | JUMP_IF_TRUE_OR_POP, jump_addr[-2] 3175 | ): 3176 | return None 3177 | # Generator 3178 | if ( 3179 | jump_addr.opcode is not END_FINALLY 3180 | and jump_addr[1] 3181 | and jump_addr[1].opcode is JUMP_ABSOLUTE 3182 | ): 3183 | return None 3184 | 3185 | next_addr = addr[1] 3186 | while next_addr and next_addr < jump_addr: 3187 | if next_addr.opcode in stmt_opcodes: 3188 | break 3189 | if next_addr.opcode in pop_jump_if_opcodes: 3190 | next_jump_addr = next_addr.jump() 3191 | if next_jump_addr > jump_addr or ( 3192 | next_jump_addr == jump_addr 3193 | and jump_addr[-1].opcode in else_jump_opcodes 3194 | ): 3195 | return None 3196 | 3197 | if next_addr.opcode in ( 3198 | JUMP_IF_FALSE_OR_POP, 3199 | JUMP_IF_TRUE_OR_POP, 3200 | ): 3201 | next_jump_addr = next_addr.jump() 3202 | if next_jump_addr > jump_addr or ( 3203 | next_jump_addr == jump_addr 3204 | and jump_addr[-1].opcode in else_jump_opcodes 3205 | ): 3206 | return None 3207 | next_addr = next_addr[1] 3208 | # if there are no nested conditionals and no else clause, write the true portion and jump ahead to the end of the conditional 3209 | cond = self.pop_popjump() 3210 | end_true = jump_addr 3211 | if truthiness: 3212 | cond = PyNot(cond) 3213 | d_true = SuiteDecompiler(addr[1], end_true, self.stack[:]) 3214 | d_true.run() 3215 | stmt = IfStatement(cond, d_true.suite, None) 3216 | self.suite.add_statement(stmt) 3217 | return end_true 3218 | # Increase jump_addr to pop all previous jumps 3219 | self.push_popjump(truthiness, jump_addr[1], cond, addr) 3220 | cond = self.pop_popjump() 3221 | end_true = jump_addr[-1] 3222 | if truthiness: 3223 | last_pj = addr.seek_back(pop_jump_if_opcodes) 3224 | if ( 3225 | last_pj 3226 | and last_pj.arg == addr.arg 3227 | and isinstance(cond, PyBooleanAnd) 3228 | or isinstance(cond, PyBooleanOr) 3229 | ): 3230 | if last_pj.opcode is not addr.opcode: 3231 | cond.right = PyNot(cond.right) 3232 | else: 3233 | cond = PyNot(cond) 3234 | 3235 | if end_true.opcode is RETURN_VALUE: 3236 | end_false = jump_addr.seek_forward(RETURN_VALUE) 3237 | if ( 3238 | end_false 3239 | and end_false[2] 3240 | and end_false[2].opcode is RETURN_VALUE 3241 | ): 3242 | d_true = SuiteDecompiler(addr[1], jump_addr, self.stack[:]) 3243 | d_true.run() 3244 | d_false = SuiteDecompiler(jump_addr, end_false[1], self.stack[:]) 3245 | d_false.run() 3246 | self.suite.add_statement( 3247 | IfStatement(cond, d_true.suite, d_false.suite) 3248 | ) 3249 | 3250 | return end_false[1] 3251 | 3252 | if ( 3253 | end_true.opcode is RAISE_VARARGS 3254 | and addr[1].opcode is LOAD_GLOBAL 3255 | ): 3256 | assert_addr = addr[1] 3257 | if ( 3258 | assert_addr.code.names[assert_addr.arg].name 3259 | == 'AssertionError' 3260 | ): 3261 | cond = ( 3262 | cond.operand 3263 | if isinstance(cond, PyNot) 3264 | else PyNot(cond) 3265 | ) 3266 | d_true = SuiteDecompiler(addr[1], end_true, self.stack[:]) 3267 | d_true.run() 3268 | assert_pop = d_true.stack.pop() 3269 | assert_args = ( 3270 | assert_pop.args 3271 | if isinstance(assert_pop, PyCallFunction) 3272 | else [] 3273 | ) 3274 | assert_arg_str = ', '.join( 3275 | map(str, [cond, *assert_args]) 3276 | ) 3277 | self.suite.add_statement( 3278 | SimpleStatement(f'assert {assert_arg_str}') 3279 | ) 3280 | return jump_addr 3281 | if self.is_loop: 3282 | # - If the true clause ends in return and we're not in 3283 | # another loop, break instead 3284 | # - If the true clause jumps forward and it's past loop 3285 | # boundaries, break instead 3286 | if self.convert_return_break and end_true.opcode is RETURN_VALUE: 3287 | d_true = SuiteDecompiler(addr[1], end_true, self.stack[:]) 3288 | d_true.run() 3289 | d_true.suite.add_statement(SimpleStatement("break")) 3290 | self.suite.add_statement( 3291 | IfStatement(cond, d_true.suite, Suite()) 3292 | ) 3293 | return jump_addr 3294 | elif end_true.opcode is JUMP_FORWARD: 3295 | end_false = end_true.jump() 3296 | instr = self.wrap_addr(end_false)[-1] 3297 | if instr.opcode is JUMP_ABSOLUTE: 3298 | d_true = SuiteDecompiler(addr[1], end_true, self.stack[:]) 3299 | d_true.run() 3300 | d_true.suite.add_statement(SimpleStatement("break")) 3301 | self.suite.add_statement( 3302 | IfStatement(cond, d_true.suite, Suite()) 3303 | ) 3304 | return jump_addr 3305 | # - If the true clause ends in return, make sure it's included 3306 | # - If the true clause ends in RAISE_VARARGS, then it's an 3307 | # assert statement. For now I just write it as a raise within 3308 | # an if (see below) 3309 | if end_true.opcode in (RETURN_VALUE, RAISE_VARARGS, POP_TOP): 3310 | d_true = SuiteDecompiler(addr[1], jump_addr, self.stack[:]) 3311 | d_true.run() 3312 | self.suite.add_statement( 3313 | IfStatement(cond, d_true.suite, Suite()) 3314 | ) 3315 | return jump_addr 3316 | if is_chained and addr[1].opcode is JUMP_ABSOLUTE: 3317 | end_true = end_true[-2] 3318 | d_true = SuiteDecompiler(addr[1], end_true, self.stack[:]) 3319 | d_true.run() 3320 | l = None 3321 | if addr[1].opcode is JUMP_ABSOLUTE: 3322 | j = addr[1].jump() 3323 | if last_loop != None and len(last_loop) > 1: 3324 | l = last_loop[1] 3325 | while l != None and l.opcode not in stmt_opcodes: 3326 | if l == j: 3327 | d_true.suite.add_statement( 3328 | SimpleStatement('continue') 3329 | ) 3330 | 3331 | self.suite.add_statement( 3332 | IfStatement(cond, d_true.suite, None) 3333 | ) 3334 | return addr[2] 3335 | l = l[1] 3336 | 3337 | if jump_addr.opcode is POP_BLOCK and not end_of_loop: 3338 | # It's a while loop 3339 | stmt = WhileStatement(cond, d_true.suite) 3340 | self.suite.add_statement(stmt) 3341 | return jump_addr[1] 3342 | # It's an if-else (expression or statement) 3343 | if end_true.opcode is JUMP_FORWARD: 3344 | end_false = end_true.jump() 3345 | elif end_true.opcode is JUMP_ABSOLUTE: 3346 | end_false = end_true.jump() 3347 | if end_false.opcode is FOR_ITER: 3348 | # We are in a for-loop with nothing after the else-suite 3349 | end_false = end_false.jump()[-1] 3350 | elif "SETUP_LOOP" in globals() and self.wrap_addr(end_false)[-1].opcode is SETUP_LOOP: 3351 | # We are in a while-loop with nothing after the else-suite 3352 | end_false = self.wrap_addr(end_false)[-1].jump()[-1] 3353 | elif self.is_loop: 3354 | end_false = jump_addr 3355 | if end_false.opcode is RETURN_VALUE: 3356 | end_false = self.wrap_addr(end_false)[1] 3357 | elif end_true.opcode is RETURN_VALUE: 3358 | # find the next RETURN_VALUE 3359 | end_false = jump_addr 3360 | while end_false.opcode is not RETURN_VALUE: 3361 | end_false = end_false[1] 3362 | end_false = end_false[1] 3363 | elif end_true.opcode is BREAK_LOOP: 3364 | # likely in a loop in a try/except 3365 | end_false = jump_addr 3366 | else: 3367 | end_false = jump_addr 3368 | # # normal statement 3369 | # raise Exception("#ERROR: Unexpected statement: {} | {}\n".format(end_true, jump_addr, jump_addr[-1])) 3370 | # # raise Unknown 3371 | # jump_addr = end_true[-2] 3372 | # stmt = IfStatement(cond, d_true.suite, None) 3373 | # self.suite.add_statement(stmt) 3374 | # return jump_addr or self.END_NOW 3375 | d_false = SuiteDecompiler(jump_addr, end_false, self.stack[:]) 3376 | d_false.run() 3377 | stack_level = len(self.stack) 3378 | if len(d_true.stack) == len(d_false.stack) > stack_level: 3379 | assert not (d_true.suite or d_false.suite) 3380 | # this happens in specific if else conditions with assigments 3381 | true_expr = d_true.stack.pop() 3382 | false_expr = d_false.stack.pop() 3383 | self.stack.push(PyIfElse(cond, true_expr, false_expr)) 3384 | else: 3385 | stmt = IfStatement(cond, d_true.suite, d_false.suite) 3386 | self.suite.add_statement(stmt) 3387 | return end_false or self.END_NOW 3388 | 3389 | def POP_JUMP_IF_FALSE(self, addr, target): 3390 | return self.POP_JUMP_IF(addr, target, truthiness=False) 3391 | 3392 | def POP_JUMP_IF_TRUE(self, addr, target): 3393 | return self.POP_JUMP_IF(addr, target, truthiness=True) 3394 | 3395 | def addr2list(self, addr): 3396 | import attrdict, inspect 3397 | 3398 | if not (addr[-1] is None): 3399 | return addr 3400 | 3401 | a = dict( 3402 | (idx, addr.code[addr.index + idx]) 3403 | for idx in range(-5, len(list(addr.code)) - addr.index) 3404 | ) 3405 | [a.__setitem__(*pair) for pair in inspect.getmembers(addr)] 3406 | wa = attrdict() 3407 | wa.update(a) 3408 | if a[-1] is None: 3409 | wa.update({-1: Address(addr.code, addr.index - 1)}) 3410 | return wa 3411 | 3412 | def wrap_addr(self, addr): 3413 | if not isinstance(addr, Address): 3414 | return addr 3415 | if addr[-1] is None: 3416 | return self.addr2list(addr) 3417 | return addr 3418 | 3419 | def JUMP_ABSOLUTE(self, addr, target): 3420 | import operator, sys 3421 | 3422 | if 'debug' in globals(): 3423 | print("*** JUMP ABSOLUTE ***", addr) 3424 | globals()['debug'] += [ 3425 | ( 3426 | self.JUMP_ABSOLUTE, 3427 | (addr, target), 3428 | sys._current_frames(), 3429 | ) 3430 | ] 3431 | 3432 | # return addr.jump() 3433 | 3434 | # TODO: print out continue if not final jump 3435 | jump_addr = addr.jump() 3436 | 3437 | if "SETUP_LOOP" in globals() and self.wrap_addr(jump_addr)[-1].opcode is SETUP_LOOP: 3438 | end_addr = jump_addr + self.wrap_addr(jump_addr)[-1].arg 3439 | 3440 | last_jump = self.scan_for_final_jump( 3441 | jump_addr, self.wrap_addr(end_addr)[-1] 3442 | ) 3443 | if last_jump != addr: 3444 | self.suite.add_statement(SimpleStatement('continue')) 3445 | pass 3446 | 3447 | # 3448 | # For loops 3449 | # 3450 | 3451 | def GET_ITER(self, addr): 3452 | pass 3453 | 3454 | def FOR_ITER(self, addr: Address, delta): 3455 | if addr[-1] and addr[-1].opcode is RETURN_VALUE: 3456 | # Dead code 3457 | return self.END_NOW 3458 | iterable = self.stack.pop() 3459 | jump_addr = addr.jump() 3460 | end_body = jump_addr 3461 | if end_body.opcode is not POP_BLOCK: 3462 | end_body = end_body[-1] 3463 | d_body = SuiteDecompiler(addr[1], end_body) 3464 | d_body.is_loop = sys.version_info > (3, 7) 3465 | d_body.convert_return_break = not self.is_loop 3466 | for_stmt = ForStatement(iterable) 3467 | d_body.stack.push(*self.stack._stack, for_stmt, for_stmt) 3468 | d_body.run() 3469 | for_stmt.body = d_body.suite 3470 | loop = None 3471 | outer_loop = None 3472 | if "SETUP_LOOP" in globals(): 3473 | loop = addr.seek_back(SETUP_LOOP) 3474 | while loop: 3475 | if "SETUP_LOOP" in globals(): 3476 | outer_loop = loop.seek_back(SETUP_LOOP) 3477 | if outer_loop: 3478 | if outer_loop.jump().addr < loop.addr: 3479 | break 3480 | else: 3481 | loop = outer_loop 3482 | else: 3483 | break 3484 | end_addr = jump_addr 3485 | if loop: 3486 | end_of_loop = loop.jump()[-1] 3487 | if end_of_loop.opcode is not POP_BLOCK: 3488 | else_start = end_of_loop.seek_back(POP_BLOCK) 3489 | d_else = SuiteDecompiler(else_start, loop.jump()) 3490 | d_else.run() 3491 | for_stmt.else_body = d_else.suite 3492 | end_addr = loop.jump() 3493 | self.suite.add_statement(for_stmt) 3494 | return end_addr 3495 | 3496 | # Function creation 3497 | 3498 | def MAKE_FUNCTION_OLD(self, addr, argc, is_closure=False): 3499 | testType = self.stack.pop().val 3500 | if isinstance(testType, str): 3501 | testType = self.stack.pop().val 3502 | code = Code(testType, self.code, is_comp = testType.co_name in code_map) 3503 | closure = self.stack.pop() if is_closure else None 3504 | # parameter annotation objects 3505 | paramobjs = {} 3506 | paramcount = (argc >> 16) & 0x7FFF 3507 | if paramcount: 3508 | paramobjs = dict( 3509 | zip( 3510 | self.stack.pop().val, 3511 | self.stack.pop(paramcount - 1), 3512 | ) 3513 | ) 3514 | # default argument objects in positional order 3515 | defaults = self.stack.pop(argc & 0xFF) 3516 | # pairs of name and default argument, with the name just below the object on the stack, for keyword-only parameters 3517 | kwdefaults = {} 3518 | for i in range((argc >> 8) & 0xFF): 3519 | k, v = self.stack.pop(2) 3520 | if hasattr(k, 'name'): 3521 | kwdefaults[k.name] = v 3522 | elif hasattr(k, 'val'): 3523 | kwdefaults[k.val] = v 3524 | else: 3525 | kwdefaults[str(k)] = v 3526 | func_maker = code_map.get(code.name, DefStatement) 3527 | self.stack.push( 3528 | func_maker(code, defaults, kwdefaults, closure, paramobjs) 3529 | ) 3530 | 3531 | def MAKE_FUNCTION_NEW(self, addr, argc, is_closure=False): 3532 | testType = self.stack.pop().val 3533 | if isinstance(testType, str): 3534 | testType = self.stack.pop().val 3535 | code = Code(testType, self.code, is_comp = testType.co_name in code_map) 3536 | closure = self.stack.pop() if is_closure else None 3537 | annotations = {} 3538 | kwdefaults = {} 3539 | defaults = {} 3540 | if argc & 8: 3541 | annotations = list(self.stack.pop()) 3542 | if argc & 4: 3543 | annotations = self.stack.pop() 3544 | if isinstance(annotations, PyDict): 3545 | annotations = { 3546 | str(k[0].val).replace('\'', ''): str(k[1]) 3547 | for k in annotations.items 3548 | } 3549 | if argc & 2: 3550 | kwdefaults = self.stack.pop() 3551 | if isinstance(kwdefaults, PyDict): 3552 | kwdefaults = { 3553 | str(k[0].val): str( 3554 | k[1] 3555 | if isinstance(k[1], PyExpr) 3556 | else PyConst(k[1]) 3557 | ) 3558 | for k in kwdefaults.items 3559 | } 3560 | if not kwdefaults: 3561 | kwdefaults = {} 3562 | if argc & 1: 3563 | defaults = list( 3564 | map( 3565 | lambda x: str( 3566 | x if isinstance(x, PyExpr) else PyConst(x) 3567 | ), 3568 | self.stack.pop(), 3569 | ) 3570 | ) 3571 | func_maker = code_map.get(code.name, DefStatement) 3572 | self.stack.push( 3573 | func_maker( 3574 | code, 3575 | defaults, 3576 | kwdefaults, 3577 | closure, 3578 | annotations, 3579 | annotations, 3580 | ) 3581 | ) 3582 | 3583 | def MAKE_FUNCTION(self, addr, argc, is_closure=False): 3584 | if sys.version_info < (3, 6): 3585 | self.MAKE_FUNCTION_OLD(addr, argc, is_closure) 3586 | else: 3587 | self.MAKE_FUNCTION_NEW(addr, argc, is_closure) 3588 | 3589 | def LOAD_CLOSURE(self, addr, i): 3590 | # Push the varname. It doesn't matter as it is not used for now. 3591 | self.stack.push(self.code.derefnames[i]) 3592 | 3593 | def MAKE_CLOSURE(self, addr, argc): 3594 | self.MAKE_FUNCTION(addr, argc, is_closure=True) 3595 | 3596 | # 3597 | # Raising exceptions 3598 | # 3599 | 3600 | def RAISE_VARARGS(self, addr, argc): 3601 | # TODO: find out when argc is 2 or 3 3602 | # Answer: In Python 3, only 0, 1, or 2 argument (see PEP 3109) 3603 | if argc == 0: 3604 | self.write("raise") 3605 | elif argc == 1: 3606 | exception = self.stack.pop() 3607 | self.write("raise {}", exception) 3608 | elif argc == 2: 3609 | from_exc, exc = self.stack.pop(), self.stack.pop() 3610 | self.write("raise {} from {}".format(exc, from_exc)) 3611 | else: 3612 | raise Unknown 3613 | 3614 | def EXTENDED_ARG(self, addr, ext): 3615 | # self.write("# ERROR: {} : {}".format(addr, ext) ) 3616 | pass 3617 | 3618 | def WITH_CLEANUP(self, addr, *args, **kwargs): 3619 | # self.write("# ERROR: {} : {}".format(addr, args)) 3620 | pass 3621 | 3622 | def WITH_CLEANUP_START(self, addr, *args, **kwargs): 3623 | pass 3624 | 3625 | def WITH_CLEANUP_FINISH(self, addr, *args, **kwargs): 3626 | jaddr = addr.jump() 3627 | return jaddr 3628 | 3629 | # Formatted string literals 3630 | def FORMAT_VALUE(self, addr, flags): 3631 | formatter = '' 3632 | if flags == 1: 3633 | formatter = '!s' 3634 | elif flags == 2: 3635 | formatter = '!r' 3636 | elif flags == 3: 3637 | formatter = '!a' 3638 | elif flags == 4: 3639 | formatter = f':{self.stack.pop().val}' 3640 | val = self.stack.pop() 3641 | f = PyFormatValue(val) 3642 | f.formatter = formatter 3643 | self.stack.push(f) 3644 | 3645 | def BUILD_STRING(self, addr, c): 3646 | params = self.stack.pop(c) 3647 | self.stack.push(PyFormatString(params)) 3648 | 3649 | # Coroutines 3650 | def GET_AWAITABLE(self, addr: Address): 3651 | func: AwaitableMixin = self.stack.pop() 3652 | func.is_awaited = True 3653 | self.stack.push(func) 3654 | yield_op = addr.seek_forward(YIELD_FROM) 3655 | return yield_op[1] 3656 | 3657 | def BEFORE_ASYNC_WITH(self, addr: Address): 3658 | with_addr = addr.seek_forward(SETUP_ASYNC_WITH) 3659 | end_with = with_addr.jump() 3660 | with_stmt = WithStatement(self.stack.pop()) 3661 | with_stmt.is_async = True 3662 | d_with = SuiteDecompiler(addr[1], end_with) 3663 | d_with.stack.push(with_stmt) 3664 | d_with.run() 3665 | with_stmt.suite = d_with.suite 3666 | self.suite.add_statement(with_stmt) 3667 | if sys.version_info <= (3, 4): 3668 | assert end_with.opcode is WITH_CLEANUP 3669 | assert end_with[1].opcode is END_FINALLY 3670 | return end_with[2] 3671 | else: 3672 | assert end_with.opcode is WITH_CLEANUP_START 3673 | assert end_with[1].opcode is GET_AWAITABLE 3674 | assert end_with[4].opcode is WITH_CLEANUP_FINISH 3675 | return end_with[5] 3676 | 3677 | def SETUP_ASYNC_WITH(self, addr: Address, arg): 3678 | pass 3679 | 3680 | def GET_AITER(self, addr: Address): 3681 | return addr[2] 3682 | 3683 | def GET_ANEXT(self, addr: Address): 3684 | iterable = self.stack.pop() 3685 | for_stmt = ForStatement(iterable) 3686 | for_stmt.is_async = True 3687 | jump_addr = addr[-1].jump() 3688 | d_body = SuiteDecompiler(addr[3], jump_addr[-1]) 3689 | d_body.stack.push(for_stmt) 3690 | d_body.run() 3691 | jump_addr = jump_addr[-1].jump() 3692 | new_start = jump_addr 3693 | new_end = jump_addr[-2].jump()[-1] 3694 | d_body.start_addr = new_start 3695 | 3696 | d_body.end_addr = new_end 3697 | 3698 | d_body.run() 3699 | 3700 | for_stmt.body = d_body.suite 3701 | self.suite.add_statement(for_stmt) 3702 | new_end = new_end.seek_forward(POP_BLOCK) 3703 | return new_end 3704 | 3705 | 3706 | SuiteDecompiler.RESUME = SuiteDecompiler.__dict__["CONTINUE_LOOP"] 3707 | SuiteDecompiler.CALL_NO_KW = SuiteDecompiler.__dict__[ 3708 | "CALL_FUNCTION_VAR" 3709 | ] 3710 | 3711 | 3712 | def make_dynamic_instr(cls): 3713 | def method(self, addr): 3714 | cls.instr(self.stack) 3715 | 3716 | return method 3717 | 3718 | 3719 | # Create unary operators types and opcode handlers 3720 | for op, name, ptn, prec in unary_ops: 3721 | name = 'Py' + name 3722 | tp = type(name, (PyUnaryOp,), dict(pattern=ptn, precedence=prec)) 3723 | globals()[name] = tp 3724 | setattr(SuiteDecompiler, op, make_dynamic_instr(tp)) 3725 | 3726 | # Create binary operators types and opcode handlers 3727 | for op, name, ptn, prec, inplace_ptn in binary_ops: 3728 | # Create the binary operator 3729 | tp_name = 'Py' + name 3730 | tp = globals().get(tp_name, None) 3731 | if tp is None: 3732 | tp = type( 3733 | tp_name, (PyBinaryOp,), dict(pattern=ptn, precedence=prec) 3734 | ) 3735 | globals()[tp_name] = tp 3736 | 3737 | setattr(SuiteDecompiler, 'BINARY_' + op, make_dynamic_instr(tp)) 3738 | # Create the in-place operation 3739 | if inplace_ptn is not None: 3740 | inplace_op = "INPLACE_" + op 3741 | tp_name = 'InPlace' + name 3742 | tp = type(tp_name, (InPlaceOp,), dict(pattern=inplace_ptn)) 3743 | globals()[tp_name] = tp 3744 | setattr(SuiteDecompiler, inplace_op, make_dynamic_instr(tp)) 3745 | 3746 | if __name__ == "__main__": 3747 | import sys 3748 | 3749 | if len(sys.argv) < 2: 3750 | print( 3751 | 'USAGE: {} [START [END=-1]]'.format( 3752 | sys.argv[0] 3753 | ) 3754 | ) 3755 | sys.exit(1) 3756 | 3757 | start = 0 3758 | end = -1 3759 | if len(sys.argv) > 2: 3760 | start = int(sys.argv[2]) 3761 | if len(sys.argv) > 3: 3762 | end = int(sys.argv[3]) 3763 | with open(sys.argv[1], "rb") as stream: 3764 | code_obj = read_code(stream) 3765 | code = Code(code_obj) 3766 | dc = SuiteDecompiler( 3767 | code.address(code.instr_list[start][0]), 3768 | code.address(code.instr_list[end][0]), 3769 | ) 3770 | try: 3771 | dc.run() 3772 | except Exception as e: 3773 | print("Exception during dc.run():", e, file=sys.stderr) 3774 | 3775 | s = IndentString() 3776 | dc.suite.display(s) 3777 | 3778 | print("\x0a".join(s.lines)) 3779 | -------------------------------------------------------------------------------- /unpyc/unpyc3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unpyc import unpyc3 3 | sys.modules.update( 4 | { 5 | "unpyc3": unpyc3 6 | } 7 | ) 8 | from unpyc.unpyc3 import * 9 | --------------------------------------------------------------------------------