;ii7dZddlZddlZddlmZddlmZddlmZe edej Z de fdZ ej ejzfd efd Zd ed efd Zejsd ZeZdZdZdZdZdZnRejsdZdZdZdZdZdZedZdZn0d ZeZdZdZdZdZejredZdZnedZdZedezdzezdzezd zezd!zezd"zezd#zezd$zezd%zZed&ezd'zezd(zZd)efd*Zd+ed,ed-e fd.Z d+ed,ed-e!fd/Z"d,ed-e!fd0Z#d6d1e$d,ed-e%fd2Z&d,ed-e%fd3Z'd4e fd5Z(dS)7aX Tools for searching bytecode for key statements that indicate the need for additional resources, such as data files and package metadata. By *bytecode* I mean the ``code`` object given by ``compile()``, accessible from the ``__code__`` attribute of any non-builtin function or, in PyInstallerLand, the ``PyiModuleGraph.node("some.module").code`` attribute. The best guide for bytecode format I have found is the disassembler reference: https://docs.python.org/3/library/dis.html This parser implementation aims to combine the flexibility and speed of regex with the clarity of the output of ``dis.dis(code)``. It has not achieved the 2nd, but C'est la vie... The biggest clarity killer here is the ``EXTENDED_ARG`` opcode which can appear almost anywhere and therefore needs to be tiptoed around at every step. If this code needs to expand significantly, I would recommend an upgrade to a regex-based grammar parsing library such as Reparse. This way, little steps like unpacking ``EXTENDED_ARGS`` can be defined once then simply referenced forming a nice hierarchy rather than copied everywhere its needed. N)CodeType)Pattern)compat _all_opmapxc\tjtt|gS)zG Get a regex-escaped opcode byte from its human readable name. )reescapebytesopmap)rs OC:\PYTHON\MyICR_Workspace\venv\Lib\site-packages\PyInstaller/depend/bytecode.py_instruction_to_regexr)s" 9UE!H:&& ' ''patternct|tsJtjdd|}tj||S)a A regex-powered Python bytecode matcher. ``bytecode_regex`` provides a very thin wrapper around :func:`re.compile`. * Any opcode names wrapped in backticks are substituted for their corresponding opcode bytes. * Patterns are compiled in VERBOSE mode by default so that whitespace and comments may be used. This aims to mirror the output of :func:`dis.dis`, which is far more readable than looking at raw byte strings. s`(\w+)`cPt|dS)N)rdecode)ms r z bytecode_regex..@s'! 66r)flags) isinstancer r subcompile)rrs r bytecode_regexr0sP gu % %%% %f66G :gU + + ++rstringc#&Kt|tsJt|}||} |D]N}|dzdkr|V"|||dz}ndST)a Call ``pattern.finditer(string)``, but remove any matches beginning on an odd byte (i.e., matches where match.start() is not a multiple of 2). This should be used to avoid false positive matches where a bytecode pair's argument is mistaken for an opcode. TrrN)rr _cleanup_bytecode_stringfinditerstart)rrmatchesmatchs r r r Fs fe $ $$$ $ %f - -Fv&&G   E{{}}q A%% "**65;;==13DEE E rs`EXTENDED_ARG`s%`LOAD_NAME`|`LOAD_GLOBAL`|`LOAD_FAST`s`LOAD_ATTR`|`LOAD_METHOD`s `LOAD_CONST`s0`CALL_FUNCTION`|`CALL_METHOD`|`CALL_FUNCTION_EX`c|SNbytecodes r rrpsrs#`EXTENDED_ARG`|`EXTENDED_ARG_QUICK`s"`EXTENDED_ARG``EXTENDED_ARG_QUICK`s`PRECALL`|`CALL_FUNCTION_EX`s(`CACHE`.)|(..)c8td|SNs\2_cache_instruction_filterrr's r rrs(,,VX>>>rs `LOAD_ATTR`s`CALL`|`CALL_FUNCTION_EX`s(`CACHE`.)|(`PUSH_NULL`.)|(..)c8td|S)Ns\3r+r's r rr,00BB Brc8td|Sr*r+r's r rrr.rs # Matches `global_function('some', 'constant', 'arguments')`. # Load the global function. In code with >256 of names, this may require extended name references. ( (?:(?:s).)* (?:(?:s).) ) # For foo.bar.whizz(), the above is the 'foo', below is the 'bar.whizz' (one opcode per name component, each # possibly preceded by name reference extension). ( (?: (?:(?:s).)* (?:s). )* ) # Load however many arguments it takes. These (for now) must all be constants. # Again, code with >256 constants may need extended enumeration. ( (?: (?:(?:s).)* (?:s). )* ) # Call the function. If opcode is CALL_FUNCTION_EX, the parameter are flags. For other opcodes, the parameter # is the argument count (which may be > 256). ( (?:(?:s).)* (?:s ). ) s:( # Arbitrary number of EXTENDED_ARG pairs. (?:(?:sG).)* # Followed by some other instruction (usually a LOAD). [^s]. ) extended_argscJt|ddddS)aQ Unpack the (extended) integer used to reference names or constants. The input should be a bytecode snippet of the following form:: EXTENDED_ARG ? # Repeated 0-4 times. LOAD_xxx ? # Any of LOAD_NAME/LOAD_CONST/LOAD_METHOD/... Each ? byte combined together gives the number we want. rNrbig)int from_bytes)r0s r extended_argumentsr5s# >>-1-u 5 55rrawcodereturnct|}|dtdkr |j|S|dtdkr |j|Stjr'|dtdkr|j|dz Stjr'|dtdkr|j|dz S|j|S)z3 Parse an (extended) LOAD_xxx instruction. LOAD_FAST LOAD_CONST LOAD_GLOBALr LOAD_ATTR)r5r co_varnames co_constsris_py311co_namesis_py312)r6r7indexs r loadrEs s # #E  2w% $$$&& 2w% %%%~e$$ )3r7eM&:::}UaZ(( )3r7eK&888}UaZ(( = rcPfdt|DS)z Parse multiple consecutive LOAD_xxx instructions. Or load() in a for loop. May be used to unpack a function's parameters or nested attributes ``(foo.bar.pop.whack)``. c0g|]}t|Sr&)rE).0ir7s r zloads..s! G G GaDDMM G G Gr)_extended_arg_bytecodefindall)r6r7s `r loadsrMs/ H G G G#9#A#A##F#F G G GGrcdg}tt|jD]}|\}}}}t ||}t ||}d|g|z}t ||}|dtdkr[t|}|dkrt|dkst|dtst|d}n#t|} | t|kr| ||f|S)zJ Scan a code object for all function calls on constant arguments. .rCALL_FUNCTION_EXr)r _call_function_bytecodeco_codegroupsrErMjoinr r5lenrtuplelistappend) r7outr# function_rootmethodsargs function_callfunctionr arg_counts r function_callsr`s( C14<@@%%6;llnn3 wm ]D11 &&88]Og566T4    u%78 8 8&}55Ezz4yyA~~ZQ%?%?~Q==DD*=99ICII%% Hd#$$$$ Jrsearchc|i}||vr>||||<|jD](}t|trt|||)|S)zm Apply a search function to a code object, recursing into child code objects (function definitions). )r@rrsearch_recursively)rar7_memoconsts r rcrc.se } 5fTlld ^ 9 9E%** 9"65%888 Lrc,tt|S)z Scan a code object for function calls on constant arguments, recursing into function definitions and bodies of comprehension loops. )rcr`)r7s r recursive_function_callsrg<s nd 3 33r full_namec#K|d}|r%d|V|dd}|#dSdS)aList possible aliases of a fully qualified Python name. >>> list(any_alias("foo.bar.wizz")) ['foo.bar.wizz', 'bar.wizz', 'wizz'] This crudely allows us to capture uses of wizz() under any of :: import foo foo.bar.wizz() :: from foo import bar bar.wizz() :: from foo.bar import wizz wizz() However, it will fail for any form of aliases and quite likely find false matches. rOrN)splitrT)rhpartss r any_aliasrlDsb& OOC E hhuooabb  rr%))__doc__disr typesrtypingr PyInstallerrgetattrr strrVERBOSEDOTALLr rr rA_OPCODES_EXTENDED_ARG_OPCODES_EXTENDED_ARG2_OPCODES_FUNCTION_GLOBAL_OPCODES_FUNCTION_LOAD_OPCODES_FUNCTION_ARGS_OPCODES_FUNCTION_CALLrrCr,is_py313rQrKr5rErWrMr`callabledictrcrgrlr&rr rs"  \39--(S((((*,bi)?,,E,,,,,guB6C.2H:-Q +C DCH:-= !//B C C???? /2H,-: C$2N3U$V$V! C C C C%3N3F$G$G! C C C ). ' ' *  * * - ) ),''*,*-*,--.(/(.+/<(=(<+ = >&?&>)?!##L(%&)  # # &  6e 6 6 6 6 e 8     8HuHHHHHHH&&d&&&&R  x x     4844444r