1- # (C) Copyright 2016-2017, 2019-2021, 2023 by Rocky Bernstein
1+ # (C) Copyright 2016-2017, 2019-2021, 2023-2024
2+ # by Rocky Bernstein
23#
34# This program is free software; you can redistribute it and/or
45# modify it under the terms of the GNU General Public License
2021of stack usage.
2122"""
2223
23- from typing import Tuple
24+ from typing import Optional , Tuple
2425
2526import xdis .opcodes .opcode_35 as opcode_35
2627from xdis .opcodes .base import (
@@ -231,34 +232,7 @@ def format_BUILD_MAP_UNPACK_WITH_CALL(count):
231232# since they make use of the information there.
232233
233234
234- def extended_format_CALL_METHOD (opc , instructions ) -> str :
235- """Inst should be a "LOAD_METHOD" instruction. Looks in `instructions`
236- to see if we can find a method name. If not we'll return "".
237-
238- """
239- # From opcode description: Loads a method named co_names[namei] from the TOS object.
240- # Sometimes the method name is in the stack arg positions back.
241- call_method_inst = instructions [0 ]
242- assert call_method_inst .opname == "CALL_METHOD"
243- method_pos = call_method_inst .arg + 1
244- assert len (instructions ) >= method_pos
245- s = ""
246- for inst in instructions [1 :method_pos ]:
247- # Make sure we are in the same basic block
248- # and ... ?
249- if inst .opname in ("CALL_METHOD" ,) or inst .is_jump_target :
250- break
251- pass
252- else :
253- if instructions [method_pos ].opname == "LOAD_METHOD" :
254- s += "%s: " % instructions [method_pos ].argrepr
255- pass
256- pass
257- s += format_CALL_FUNCTION (call_method_inst .arg )
258- return s
259-
260-
261- def extended_format_CALL_FUNCTION36 (opc , instructions ):
235+ def extended_format_CALL_FUNCTION36 (opc , instructions ) -> Tuple [str , Optional [int ]]:
262236 """call_function_inst should be a "CALL_FUNCTION" instruction. Look in
263237 `instructions` to see if we can find a method name. If not we'll
264238 return None.
@@ -289,7 +263,7 @@ def extended_format_CALL_FUNCTION36(opc, instructions):
289263 return "" , None
290264
291265
292- def extended_format_CALL_FUNCTION_KW (opc , instructions ):
266+ def extended_format_CALL_FUNCTION_KW (opc , instructions ) -> Tuple [ str , Optional [ int ]] :
293267 """call_function_inst should be a "CALL_FUNCTION_KW" instruction. Look in
294268 `instructions` to see if we can find a method name. If not we'll
295269 return None.
@@ -298,41 +272,44 @@ def extended_format_CALL_FUNCTION_KW(opc, instructions):
298272 # From opcode description: argc indicates the total number of
299273 # positional and keyword arguments. Sometimes the function name
300274 # is in the stack arg positions back.
301- call_function_inst = instructions [0 ]
302- assert call_function_inst .opname == "CALL_FUNCTION_KW"
303- function_pos = call_function_inst .arg
304- assert len (instructions ) >= function_pos + 1
305- load_const = instructions [1 ]
306- if load_const .opname == "LOAD_CONST" and isinstance (load_const .argval , tuple ):
307- function_pos += len (load_const .argval ) + 1
308- s = ""
309- i = - 1
310- for i , inst in enumerate (instructions [2 :]):
311- if i == function_pos :
312- break
313- if inst .is_jump_target :
314- i += 1
315- break
316- # Make sure we are in the same basic block
317- # and ... ?
318- opcode = inst .opcode
319- if inst .optype in ("nargs" , "vargs" ):
320- break
321- if inst .optype != "name" :
322- function_pos += (oppop [opcode ] - oppush [opcode ]) + 1
323- if inst .opname in ("CALL_FUNCTION" , "CALL_FUNCTION_KW" ):
324- break
325- pass
275+ # From opcode description: arg_count indicates the total number of
276+ # positional and keyword arguments.
326277
327- if i == function_pos :
328- if instructions [function_pos ].opname in opc .NAME_OPS | opc .CONST_OPS :
329- if instructions [function_pos ].opname == "LOAD_ATTR" :
330- s += "."
331- s += "%s() " % instructions [function_pos ].argrepr
332- pass
333- pass
334- s += format_CALL_FUNCTION (call_function_inst .arg )
335- return s
278+ call_inst = instructions [0 ]
279+ arg_count = call_inst .argval
280+ keywords = instructions [1 ].argval
281+ s = ""
282+
283+ arglist , arg_count , i = get_arglist (instructions , 1 , arg_count )
284+
285+ if arg_count != 0 :
286+ return "" , None
287+
288+ assert i is not None
289+ if i >= len (instructions ) - 1 :
290+ return "" , None
291+
292+ fn_inst = instructions [i + 1 ]
293+ start_offset = instructions [i ].start_offset
294+ if fn_inst .opcode in opc .operator_set :
295+ if instructions [1 ].opname == "MAKE_FUNCTION" and opc .version_tuple >= (3 , 3 ):
296+ arglist [0 ] = instructions [2 ].argval
297+
298+ fn_name = fn_inst .tos_str if fn_inst .tos_str else fn_inst .argrepr
299+ # Note, 3.5 and 3.4 and before work slightly different with respect
300+ # to placement of keyword values, and order of arguments.
301+ arglist .reverse ()
302+ for i in range (len (keywords )):
303+ j = - (i + 1 )
304+ param_name = keywords [j ]
305+ arglist [j ] = f"{ param_name } ={ arglist [j ]} "
306+
307+ str_arglist = ", " .join (arglist )
308+ if len (str_arglist ) > 30 :
309+ str_arglist = str_arglist [:27 ] + "..."
310+ s = f"{ fn_name } ({ str_arglist } )"
311+ return s , start_offset
312+ return "" , None
336313
337314
338315opcode_arg_fmt = opcode_arg_fmt36 = {
@@ -355,7 +332,6 @@ def extended_format_CALL_FUNCTION_KW(opc, instructions):
355332 ** {
356333 "CALL_FUNCTION_KW" : extended_format_CALL_FUNCTION_KW ,
357334 # "CALL_FUNCTION_VAR": extended_format_CALL_FUNCTION,
358- "CALL_METHOD" : extended_format_CALL_METHOD ,
359335 "MAKE_FUNCTION" : extended_format_MAKE_FUNCTION_36 ,
360336 "RAISE_VARARGS" : extended_format_RAISE_VARARGS_older ,
361337 "STORE_ATTR" : extended_format_ATTR ,
0 commit comments