1313 AssignMulti ,
1414 BasicBlock ,
1515 Box ,
16+ CallC ,
1617 ControlOp ,
1718 DeserMaps ,
1819 Float ,
20+ GetElementPtr ,
21+ InitStatic ,
1922 Integer ,
23+ IntOp ,
2024 LoadAddress ,
2125 LoadLiteral ,
26+ LoadMem ,
27+ LoadStatic ,
2228 Register ,
29+ SetMem ,
2330 TupleSet ,
2431 Value ,
2532)
@@ -406,7 +413,7 @@ def all_values_full(args: list[Register], blocks: list[BasicBlock]) -> list[Valu
406413_NOT_REPRESENTABLE = object ()
407414
408415
409- def get_text_signature (fn : FuncIR , * , bound : bool = False ) -> str | None :
416+ def get_text_signature (fn : FuncIR , module_body : FuncIR , * , bound : bool = False ) -> str | None :
410417 """Return a text signature in CPython's internal doc format, or None
411418 if the function's signature cannot be represented.
412419 """
@@ -430,7 +437,7 @@ def get_text_signature(fn: FuncIR, *, bound: bool = False) -> str | None:
430437 )
431438 default : object = inspect .Parameter .empty
432439 if arg .optional :
433- default = _find_default_argument (arg .name , fn .blocks )
440+ default = _find_default_argument (arg .name , fn .blocks , module_body . blocks )
434441 if default is _NOT_REPRESENTABLE :
435442 # This default argument cannot be represented in a __text_signature__
436443 return None
@@ -444,16 +451,29 @@ def get_text_signature(fn: FuncIR, *, bound: bool = False) -> str | None:
444451 return f"{ fn .name } { inspect .Signature (parameters )} "
445452
446453
447- def _find_default_argument (name : str , blocks : list [BasicBlock ]) -> object :
454+ def _find_default_argument (
455+ name : str , blocks : list [BasicBlock ], static_blocks : list [BasicBlock ]
456+ ) -> object :
448457 # Find assignment inserted by gen_arg_defaults. Assumed to be the first assignment.
449458 for block in blocks :
450459 for op in block .ops :
451460 if isinstance (op , Assign ) and op .dest .name == name :
452- return _extract_python_literal (op .src )
461+ if isinstance (op .src , LoadStatic ):
462+ return _find_init_static (op .src .identifier , static_blocks )
463+ else :
464+ return _extract_python_literal (op .src , blocks )
465+ return _NOT_REPRESENTABLE
466+
467+
468+ def _find_init_static (fullname : str , blocks : list [BasicBlock ]) -> object :
469+ for block in blocks :
470+ for op in block .ops :
471+ if isinstance (op , InitStatic ) and op .identifier == fullname :
472+ return _extract_python_literal (op .value , blocks )
453473 return _NOT_REPRESENTABLE
454474
455475
456- def _extract_python_literal (value : Value ) -> object :
476+ def _extract_python_literal (value : Value , blocks : list [ BasicBlock ] ) -> object :
457477 if isinstance (value , Integer ):
458478 if is_none_rprimitive (value .type ):
459479 return None
@@ -466,10 +486,66 @@ def _extract_python_literal(value: Value) -> object:
466486 elif isinstance (value , LoadLiteral ):
467487 return value .value
468488 elif isinstance (value , Box ):
469- return _extract_python_literal (value .src )
489+ return _extract_python_literal (value .src , blocks )
470490 elif isinstance (value , TupleSet ):
471- items = tuple (_extract_python_literal (item ) for item in value .items )
491+ items = tuple (_extract_python_literal (item , blocks ) for item in value .items )
472492 if any (itm is _NOT_REPRESENTABLE for itm in items ):
473493 return _NOT_REPRESENTABLE
474494 return items
495+ elif isinstance (value , CallC ):
496+ if value .function_name == "PyList_New" :
497+ assert len (value .args ) == 1
498+ size = _extract_python_literal (value .args [0 ], blocks )
499+ if size == 0 :
500+ return []
501+ return _extract_list (value , blocks )
502+ if value .function_name == "PyDict_New" :
503+ return {}
504+ if value .function_name == "CPyDict_Build" :
505+ args = [_extract_python_literal (arg , blocks ) for arg in value .args ]
506+ if any (arg is _NOT_REPRESENTABLE for arg in args ):
507+ return _NOT_REPRESENTABLE
508+ return {k : v for k , v in zip (args [1 ::2 ], args [2 ::2 ])}
509+ if value .function_name == "PySet_New" :
510+ result = _extract_set (value , blocks )
511+ if not result :
512+ return _NOT_REPRESENTABLE # set() isn't valid in __text_signature__
513+ return result
514+ elif isinstance (value , LoadAddress ) and value .src == "_Py_EllipsisObject" :
515+ return _EllipsisLiteral ()
475516 return _NOT_REPRESENTABLE
517+
518+
519+ def _extract_list (value : CallC , blocks : list [BasicBlock ]) -> object :
520+ result = []
521+ for block in blocks :
522+ for op in block .ops :
523+ if isinstance (op , SetMem ):
524+ dest = op .dest .lhs if isinstance (op .dest , IntOp ) else op .dest
525+ if (
526+ isinstance (dest , LoadMem )
527+ and isinstance (dest .src , GetElementPtr )
528+ and dest .src .src == value
529+ ):
530+ item = _extract_python_literal (op .src , blocks )
531+ if item is _NOT_REPRESENTABLE :
532+ return _NOT_REPRESENTABLE
533+ result .append (item )
534+ return result
535+
536+
537+ def _extract_set (value : CallC , blocks : list [BasicBlock ]) -> object :
538+ result = set ()
539+ for block in blocks :
540+ for op in block .ops :
541+ if isinstance (op , CallC ) and op .function_name == "PySet_Add" and op .args [0 ] == value :
542+ item = _extract_python_literal (op .args [1 ], blocks )
543+ if item is _NOT_REPRESENTABLE :
544+ return _NOT_REPRESENTABLE
545+ result .add (item )
546+ return result
547+
548+
549+ class _EllipsisLiteral :
550+ def __repr__ (self ) -> str :
551+ return "..."
0 commit comments