test_demangler.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/python3
  2. import itertools
  3. from typing import Iterable
  4. import itanium_demangler as dm
  5. import pytest
  6. import smgdc.demangler_helpers as dh
  7. @pytest.mark.parametrize(
  8. "input_vtsym,input_fnsym,expected",
  9. [
  10. (
  11. # PASS: template class
  12. "_ZTV14CEntityFactoryI10CGunTargetE",
  13. "_ZN14CEntityFactoryI10CGunTargetE6CreateEPKc",
  14. True,
  15. ),
  16. (
  17. # PASS: namespaced class
  18. "_ZTVN6google8protobuf10TextFormat6Parser10ParserImpl20ParserErrorCollectorE",
  19. "_ZN6google8protobuf10TextFormat6Parser10ParserImpl20ParserErrorCollector8AddErrorEiiRKSs",
  20. True,
  21. ),
  22. (
  23. # PASS: qualified name in function
  24. "_ZTV13CTFWeaponBase",
  25. "_ZNK13CTFWeaponBase11GetWeaponIDEv",
  26. True,
  27. ),
  28. (
  29. # FAIL: unrelated classes
  30. "_ZTV13CTFBaseRocket",
  31. "_Z13GetScriptDescI11CBasePlayerEP17ScriptClassDesc_tPT_",
  32. False,
  33. ),
  34. (
  35. # FAIL: class in partial matching namespace
  36. "_ZTVN6google8protobuf10TextFormat17FieldValuePrinterE",
  37. "_ZN6google8protobuf10TextFormat6Parser10ParserImpl20ParserErrorCollector10AddWarningEiiRKSs",
  38. False,
  39. ),
  40. (
  41. # FAIL: specialized template and class implementation
  42. "_ZTV14CEntityFactoryI19CTFProjectile_FlareE",
  43. "_ZN19CTFProjectile_Flare14GetDataDescMapEv",
  44. False,
  45. ),
  46. ],
  47. )
  48. def test_function_in_virtual_class(input_vtsym: str, input_fnsym: str, expected: bool):
  49. """
  50. Checks if the function is for the vtable's class.
  51. Note that, obviously, this does not check that the function is actually in the vtable.
  52. """
  53. node_vtsym, node_fnsym = map(dm.parse, (input_vtsym, input_fnsym))
  54. vtable_typename = dh.extract_vtable_typename(node_vtsym)
  55. function_qualname = dh.extract_function_name(node_fnsym)
  56. assert (vtable_typename == function_qualname[: len(vtable_typename)]) == expected
  57. @pytest.mark.parametrize(
  58. "fnsym_a,fnsym_b,expected",
  59. [
  60. (
  61. # PASS: differing class, same name, same parameter type(s)
  62. "_ZN11CBaseEntity10ChangeTeamEi",
  63. "_ZN9CTFPlayer10ChangeTeamEi",
  64. True,
  65. ),
  66. (
  67. # PASS: class and a non-virtual thunk
  68. "_ZN9CTFPlayer16GetAttributeListEv",
  69. "_ZThn4764_N9CTFPlayer16GetAttributeListEv",
  70. True,
  71. ),
  72. (
  73. # FAIL: same class, same name, differing parameter type(s)
  74. "_ZN9CTFPlayer10ChangeTeamEi",
  75. "_ZN9CTFPlayer10ChangeTeamEibbb",
  76. False,
  77. ),
  78. (
  79. # FAIL: same class, differing name, same parameter type(s)
  80. "_ZN12CTFGameRules27TrackWorkshopMapsInMapCycleEv",
  81. "_ZN12CTFGameRules16LoadMapCycleFileEv",
  82. False,
  83. ),
  84. (
  85. # FAIL: specialized static methods with the same template (DataMapInit<T>)
  86. "_Z11DataMapInitI14SoundCommand_tEP9datamap_tPT_",
  87. "_Z11DataMapInitI10CKothLogicEP9datamap_tPT_",
  88. False,
  89. ),
  90. ],
  91. )
  92. def test_function_match_signature(fnsym_a: str, fnsym_b: str, expected: bool):
  93. sig_a, sig_b = map(dh.extract_method_signature, map(dm.parse, (fnsym_a, fnsym_b)))
  94. assert expected == (sig_a == sig_b)
  95. @pytest.mark.parametrize(
  96. "fnsyms,expected",
  97. [
  98. (
  99. # PASS: same class, same name, differing parameter types
  100. (
  101. "_ZN11CBaseEntity8KeyValueEPKcS1_",
  102. "_ZN11CBaseEntity8KeyValueEPKcf",
  103. "_ZN11CBaseEntity8KeyValueEPKcRK6Vector",
  104. ),
  105. True,
  106. ),
  107. (
  108. # PASS: same class, same name, differing parameter types
  109. (
  110. "_ZN24CTFWeaponBaseGrenadeProj11InitGrenadeERK6VectorS2_P20CBaseCombatCharacterRK13CTFWeaponInfo",
  111. "_ZN24CTFWeaponBaseGrenadeProj11InitGrenadeERK6VectorS2_P20CBaseCombatCharacterif",
  112. ),
  113. True,
  114. ),
  115. (
  116. # PASS: different class, same name, differing parameter types
  117. (
  118. "_ZN20CBaseCombatCharacter8FVisibleEP11CBaseEntityiPS1_",
  119. "_ZN11CBaseEntity8FVisibleERK6VectoriPPS_",
  120. ),
  121. True,
  122. ),
  123. (
  124. # PASS: same class, one is const-qualified
  125. (
  126. "_ZN11CBaseObject13CanBeUpgradedEP9CTFPlayer",
  127. "_ZNK11CBaseObject13CanBeUpgradedEv",
  128. ),
  129. True,
  130. ),
  131. (
  132. # FAIL: static function and class method
  133. (
  134. "_Z19ReloadSceneFromDiskP11CBaseEntity",
  135. "_ZN18CServerChoreoTools19ReloadSceneFromDiskEi",
  136. ),
  137. False,
  138. ),
  139. ],
  140. )
  141. def test_function_overloaded(fnsyms: Iterable[str], expected: bool):
  142. """
  143. Tests that all the given symbols are overloads of the same name.
  144. Of course, this is only inference based on symbol name.
  145. """
  146. node_syms = map(dh.extract_method_fname, map(dm.parse, fnsyms))
  147. for a, b in itertools.permutations(node_syms, 2):
  148. assert a == b
  149. @pytest.mark.parametrize(
  150. "sym,expected",
  151. [
  152. (
  153. "_ZN12CTFGameRulesD0Ev",
  154. "deleting",
  155. ),
  156. (
  157. "_ZN12CTFGameRulesD2Ev",
  158. "base",
  159. ),
  160. ],
  161. )
  162. def test_function_dtor(sym: str, expected: str):
  163. assert dh.get_dtor_type(dm.parse(sym)) == expected