
     hiJ<                        d Z ddlZddlZddlmZ ddlmZ ddlmZ  e	edej                        Z
defdZej                  ej                  z  fd	efd
Zd	edefdZej$                  sdZeZdZdZdZdZd Znqej4                  sdZdZdZdZdZdZ ed      Zd ZnMdZeZej8                  rdZndZdZej8                  rdZndZdZej:                  r ed      Zd Zn ed      Zd Z edez   d z   ez   d!z   ez   d"z   ez   d#z   ez   d$z   ez   d%z   ez   d&z   ez   d'z         Z ed(ez   d)z   ez   d*z         Zd+efd,Z d-ed.ed/efd0Z!d-ed.ed/e"fd1Z#d.ed/e"fd2Z$d8d3e%d.ed/e&fd4Z'd.ed/e&fd5Z(d6efd7Z)y)9aX  
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_opmapxc                 N    t        j                  t        t        |    g            S )zG
    Get a regex-escaped opcode byte from its human readable name.
    )reescapebytesopmap)r   s    GC:\des-py\Monitor\venv\Lib\site-packages\PyInstaller/depend/bytecode.py_instruction_to_regexr   )   s     99UE!H:&''    patternc                     t        | t              sJ t        j                  dd |       } t        j                  | |      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+)`c                 :    t        | d   j                               S )N   )r   decode)ms    r   <lambda>z bytecode_regex.<locals>.<lambda>@   s    '!6 r   )flags)
isinstancer   r	   subcompile)r   r   s     r   bytecode_regexr   0   s@     gu%%% ff6G
 ::gU++r   stringc              #      K   t        |t              sJ t        |      }| j                  |      }	 |D ]A  }|j	                         dz  dk(  r| | j                  ||j	                         dz         } n yHw)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.
       r   r   N)r   r   _cleanup_bytecode_stringfinditerstart)r   r   matchesmatchs       r   r    r    F   s      fe$$$%f-Fv&G
 	E{{}q A% "**65;;=13DE	  s   A8A:s   `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                     | S N bytecodes    r   r   r   p   s    r   s#   `EXTENDED_ARG`|`EXTENDED_ARG_QUICK`s"   `EXTENDED_ARG``EXTENDED_ARG_QUICK`s   `PRECALL`|`CALL_FUNCTION_EX`s   (`CACHE`.)|(..)c                 .    t         j                  d|       S Ns   \2_cache_instruction_filterr   r'   s    r   r   r      s    (,,VX>>r   s8   `LOAD_NAME`|`LOAD_GLOBAL`|`LOAD_FAST`|`LOAD_FAST_BORROW`s   `LOAD_ATTR`s3   `LOAD_CONST`|`LOAD_SMALL_INT`|`LOAD_CONST_IMMORTAL`s   `CALL`|`CALL_FUNCTION_EX`s   (`CACHE`.)|(`PUSH_NULL`.)|(..)c                 .    t         j                  d|       S )Ns   \3r+   r'   s    r   r   r          ,00BBr   c                 .    t         j                  d|       S r*   r+   r'   s    r   r   r      r.   r   s   
    # 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_argsc                 :    t         j                  | ddd   d      S )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.
    r   Nr   big)int
from_bytes)r0   s    r   extended_argumentsr5      s      >>-1-u55r   rawcodereturnc                 l   t        |       }| d   t        d   k(  r|j                  |   S | d   t        d   k(  r|j                  |   S t        j
                  r!| d   t        d   k(  r|j                  |dz	     S t        j                  r!| d   t        d   k(  r|j                  |dz	     S t        j                  r| d   t        d   k(  r|S t        j                  r| d   t        d   k(  r|j                  |   S t        j                  r| d   t        d	   k(  r|j                  |   S |j                  |   S )
z3
    Parse an (extended) LOAD_xxx instruction.
    	LOAD_FAST
LOAD_CONSTLOAD_GLOBALr   	LOAD_ATTRLOAD_SMALL_INTLOAD_CONST_IMMORTALLOAD_FAST_BORROW)	r5   r   co_varnames	co_constsr   is_py311co_namesis_py312is_py314)r6   r7   indexs      r   loadrI      s&   
 s#E
 2w%$$&&
2w%%%~~e$$3r7eM&::}}UaZ((3r7eK&88}}UaZ((3r7e,<&==3r7e,A&BB ~~e$$3r7e,>&?? &&==r   c                 f    t         j                  |       D cg c]  }t        ||       c}S c c}w )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)``.
    )_extended_arg_bytecodefindallrI   )r6   r7   is      r   loadsrN     s)     $:#A#A##FGaDDMGGGs   .c                    g }t        t        | j                        D ]  }|j                         \  }}}}t	        ||       }t        ||       }dj                  |g|z         }t        ||       }|d   t        d   k(  rBt        |      }|dk7  rqt        |      dk7  st        |d   t              st        |d         }nt        |      }	|	t        |      k7  r|j                  ||f        |S )zJ
    Scan a code object for all function calls on constant arguments.
    .r   CALL_FUNCTION_EXr   )r    _call_function_bytecodeco_codegroupsrI   rN   joinr   r5   lenr   tuplelistappend)
r7   outr#   function_rootmethodsargsfunction_callfunctionr   	arg_counts
             r   function_callsra     s    
 C14<<@ %6;lln3wm ]D1&88]Og56T4 u%788&}5Ez 4yA~ZQ%?Q=D*=9ICI%

Hd#$;%> Jr   searchc                     |i }||vr: | |      ||<   |j                   D ]   }t        |t              st        | ||       " |S )zm
    Apply a search function to a code object, recursing into child code objects (function definitions).
    )rC   r   r   search_recursively)rb   r7   _memoconsts       r   rd   rd   B  sS     }5Tld^^ 	9E%*"65%8	9 Lr   c                 "    t        t        |       S )z
    Scan a code object for function calls on constant arguments, recursing into function definitions and bodies of
    comprehension loops.
    )rd   ra   )r7   s    r   recursive_function_callsrh   P  s    
 nd33r   	full_namec              #   j   K   | j                  d      }|rdj                  |       |dd }|ryyw)a  List 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.
    rP   r   N)splitrU   )ri   partss     r   	any_aliasrm   X  s7     & OOC E
hhuoab	 s   .33r%   )*__doc__disr	   typesr   typingr   PyInstallerr   getattrr   strr   VERBOSEDOTALLr   r   r    rD   _OPCODES_EXTENDED_ARG_OPCODES_EXTENDED_ARG2_OPCODES_FUNCTION_GLOBAL_OPCODES_FUNCTION_LOAD_OPCODES_FUNCTION_ARGS_OPCODES_FUNCTION_CALLr   rF   r,   rG   is_py313rR   rK   r5   rI   rX   rN   ra   callabledictrd   rh   rm   r&   r   r   <module>r      s  "  	   
 	\399-(S ( *,bii)? ,E ,,g u B .2H:-Q	
 DCH:-=
 !//B C?
 /2#_ #L , "Y!1: $23U$V!	C %33F$G!	C
 )
 ''
* **- )), ''*, *-*,--. (/(.+/< (=(<+=> &?&>)?!# L (%&)


 ##
& 6e 6$ e $ 8 $  $ NHu HH H H& &d &Rx x  48 4 4 r   