op_imports.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. # (C) Copyright 2018-2019, 2021-2023, 2025 by Rocky Bernstein
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. """Facilitates for importing Python opcode maps for a given Python version"""
  17. import copy
  18. import sys
  19. from xdis.magics import canonic_python_version
  20. from xdis.opcodes import (
  21. opcode_10,
  22. opcode_11,
  23. opcode_12,
  24. opcode_13,
  25. opcode_14,
  26. opcode_15,
  27. opcode_16,
  28. opcode_20,
  29. opcode_21,
  30. opcode_22,
  31. opcode_23,
  32. opcode_24,
  33. opcode_25,
  34. opcode_26,
  35. opcode_26pypy,
  36. opcode_27,
  37. opcode_27pypy,
  38. opcode_30,
  39. opcode_31,
  40. opcode_32,
  41. opcode_32pypy,
  42. opcode_33,
  43. opcode_33pypy,
  44. opcode_34,
  45. opcode_35,
  46. opcode_35pypy,
  47. opcode_36,
  48. opcode_36pypy,
  49. opcode_37,
  50. opcode_37pypy,
  51. opcode_38,
  52. opcode_38pypy,
  53. opcode_39,
  54. opcode_39pypy,
  55. opcode_310,
  56. opcode_310pypy,
  57. opcode_311,
  58. opcode_311pypy,
  59. opcode_312,
  60. opcode_313,
  61. opcode_314,
  62. )
  63. from xdis.version_info import IS_PYPY, version_tuple_to_str
  64. # FIXME
  65. op_imports = {
  66. 1.0: opcode_10,
  67. "1.0": opcode_10,
  68. 1.1: opcode_11,
  69. "1.1": opcode_11,
  70. 1.2: opcode_12,
  71. "1.2": opcode_12,
  72. 1.3: opcode_13,
  73. "1.3": opcode_13,
  74. 1.4: opcode_14,
  75. "1.4": opcode_14,
  76. 1.5: opcode_15,
  77. "1.5": opcode_15,
  78. 1.6: opcode_16,
  79. "1.6": opcode_16,
  80. "2.0": opcode_20,
  81. 2.0: opcode_20,
  82. "2.1": opcode_21,
  83. 2.1: opcode_21,
  84. "2.2": opcode_22,
  85. 2.2: opcode_22,
  86. "2.3a0": opcode_23,
  87. 2.3: opcode_23,
  88. "2.4b1": opcode_24,
  89. 2.4: opcode_24,
  90. "2.5c2": opcode_25,
  91. 2.5: opcode_25,
  92. "2.5.0dropbox": opcode_25,
  93. "2.6a1": opcode_26,
  94. "2.6pypy": opcode_26pypy,
  95. 2.6: opcode_26,
  96. "2.7": opcode_27,
  97. 2.7: opcode_27,
  98. "2.7.18candidate1": opcode_27,
  99. "2.7pypy": opcode_27pypy,
  100. "2.7.12pypy": opcode_27pypy,
  101. "3.0": opcode_30,
  102. 3.0: opcode_30,
  103. "3.0a5": opcode_30,
  104. "3.1": opcode_31,
  105. "3.1a0+": opcode_31,
  106. 3.1: opcode_31,
  107. "3.2": opcode_32,
  108. "3.2a2": opcode_32,
  109. 3.2: opcode_32,
  110. "3.2pypy": opcode_32pypy,
  111. "3.3a4": opcode_33,
  112. 3.3: opcode_33,
  113. "3.3pypy": opcode_33pypy,
  114. "3.4": opcode_34,
  115. "3.4rc2": opcode_34,
  116. 3.4: opcode_34,
  117. "3.5": opcode_35,
  118. "3.5pypy": opcode_35pypy,
  119. "3.5.1": opcode_35,
  120. "3.5.2": opcode_35,
  121. "3.5.3": opcode_35,
  122. "3.5.4": opcode_35,
  123. 3.5: opcode_35,
  124. "3.6rc1": opcode_36,
  125. 3.6: opcode_36,
  126. "3.6pypy": opcode_36pypy,
  127. "3.6.1pypy": opcode_36pypy,
  128. "3.7.0beta3": opcode_37,
  129. "3.7.0.beta3": opcode_37,
  130. "3.7.0": opcode_37,
  131. "3.7pypy": opcode_37pypy,
  132. 3.7: opcode_37,
  133. "3.8.0alpha0": opcode_38,
  134. "3.8.0a0": opcode_38,
  135. "3.8.0a3+": opcode_38,
  136. "3.8.0alpha3": opcode_38,
  137. "3.8.0beta2": opcode_38,
  138. "3.8.0rc1+": opcode_38,
  139. "3.8.0candidate1": opcode_38,
  140. "3.8": opcode_38,
  141. "3.8pypy": opcode_38pypy,
  142. "3.8.0pypy": opcode_38pypy,
  143. "3.8.12pypy": opcode_38pypy,
  144. "3.8.13pypy": opcode_38pypy,
  145. "3.8.14pypy": opcode_38pypy,
  146. "3.8.15pypy": opcode_38pypy,
  147. "3.8.16pypy": opcode_38pypy,
  148. "3.8.17pypy": opcode_38pypy,
  149. "3.9.0alpha1": opcode_39,
  150. "3.9.0alpha2": opcode_39,
  151. "3.9.0beta5": opcode_39,
  152. "3.9": opcode_39,
  153. "3.9pypy": opcode_39pypy,
  154. "3.9.15pypy": opcode_39pypy,
  155. "3.9.16pypy": opcode_39pypy,
  156. "3.9.17pypy": opcode_39pypy,
  157. "3.9.18pypy": opcode_39pypy,
  158. 3.9: opcode_39,
  159. "3.10.0rc2": opcode_310,
  160. "3.10.b1": opcode_310,
  161. "3.10": opcode_310,
  162. "3.10pypy": opcode_310pypy,
  163. "3.10.12pypy": opcode_310pypy,
  164. "3.11": opcode_311,
  165. "3.11.0": opcode_311,
  166. "3.11.1": opcode_311,
  167. "3.11.2": opcode_311,
  168. "3.11.3": opcode_311,
  169. "3.11.4": opcode_311,
  170. "3.11.5": opcode_311,
  171. "3.11a7e": opcode_311,
  172. "3.11.13pypy": opcode_311pypy,
  173. 3.11: opcode_311,
  174. "3.12.0rc2": opcode_312,
  175. "3.12.0": opcode_312,
  176. "3.13.0rc3": opcode_313,
  177. "3.14b3": opcode_314,
  178. "3.14.0": opcode_314,
  179. "3.14": opcode_314,
  180. "3.14rc3": opcode_314,
  181. 3.14: opcode_314,
  182. }
  183. for k, v in canonic_python_version.items():
  184. if v in op_imports:
  185. op_imports[k] = op_imports[v]
  186. def get_opcode_module(version_info=None, variant=None):
  187. if version_info is None:
  188. version_info = sys.version_info
  189. if variant is None and IS_PYPY:
  190. variant = "pypy"
  191. pass
  192. pass
  193. elif isinstance(version_info, float):
  194. int_vers = int(version_info * 10)
  195. version_info = [int_vers // 10, int_vers % 10]
  196. vers_str = version_tuple_to_str(version_info)
  197. if len(version_info) > 3 and version_info[3] != "final":
  198. vers_str += version_tuple_to_str(version_info, start=3)
  199. if vers_str not in canonic_python_version:
  200. vers_str = version_tuple_to_str(version_info[:2])
  201. if variant is None:
  202. try:
  203. import platform
  204. variant = platform.python_implementation()
  205. if platform in ("Jython", "Pyston"):
  206. vers_str += variant
  207. pass
  208. except ImportError:
  209. # Python may be too old, e.g. < 2.6 or implementation may
  210. # just not have the ``platform`` attribute.
  211. pass
  212. elif variant != "Graal":
  213. vers_str += variant
  214. return op_imports[canonic_python_version[vers_str]]
  215. def remap_opcodes(op_obj, alternate_opmap):
  216. # All these lists are 255 in length, with index, ``i``, corresponding to opcode ``i``
  217. if hasattr(op_obj, "REMAPPED") and op_obj.REMAPPED:
  218. return op_obj
  219. positional_opcode_lists = [
  220. "opname", # Opcode's name
  221. "oppop", # How many items this opcode pops off the stack
  222. "oppush", # How many items this opcode pushes onto the stack
  223. ]
  224. # These lists contain all the opcodes that fit a certain description
  225. categorized_opcode_lists = [
  226. "hascompare",
  227. "hascondition",
  228. "hasconst",
  229. "hasfree",
  230. "hasjabs",
  231. "hasjrel",
  232. "haslocal",
  233. "hasname",
  234. "hasnargs",
  235. "hasvargs",
  236. "nofollow",
  237. ]
  238. new_opmap = copy.deepcopy(op_obj.opmap)
  239. new_lists = {}
  240. for list_name in positional_opcode_lists:
  241. if hasattr(op_obj, list_name):
  242. new_lists[list_name] = copy.deepcopy(getattr(op_obj, list_name))
  243. for list_name in categorized_opcode_lists:
  244. if hasattr(op_obj, list_name):
  245. new_lists[list_name] = copy.deepcopy(getattr(op_obj, list_name))
  246. new_frozensets = {}
  247. for i in dir(op_obj):
  248. item = getattr(op_obj, i)
  249. if isinstance(item, frozenset):
  250. item = list(item)
  251. new_frozensets[i] = copy.deepcopy(item)
  252. opcodes_with_args = {}
  253. for opname, opcode in op_obj.opmap.items():
  254. if opcode >= op_obj.HAVE_ARGUMENT:
  255. opcodes_with_args[opname] = opcode
  256. for opname, alt_opcode in alternate_opmap.items():
  257. if opname not in op_obj.opmap:
  258. raise KeyError(
  259. "The opname {} was not found in Python's original opmap for version {}".format(
  260. opname, op_obj.version
  261. )
  262. )
  263. else:
  264. original_opcode = op_obj.opmap[opname]
  265. new_opmap[opname] = alt_opcode
  266. if original_opcode == alt_opcode:
  267. continue
  268. if hasattr(op_obj, opname):
  269. setattr(op_obj, opname, alt_opcode)
  270. for list_name in positional_opcode_lists:
  271. if not hasattr(op_obj, list_name):
  272. continue
  273. new_opcode_list = new_lists[list_name]
  274. original_list = getattr(op_obj, list_name)
  275. new_opcode_list[alt_opcode] = original_list[original_opcode]
  276. for list_name in categorized_opcode_lists:
  277. if not hasattr(op_obj, list_name):
  278. continue
  279. new_opcode_list = new_lists[list_name]
  280. original_list = getattr(op_obj, list_name)
  281. if original_opcode in original_list:
  282. new_opcode_list[original_list.index(original_opcode)] = alt_opcode
  283. for set_name, frozen_set_list in new_frozensets.items():
  284. if original_opcode in getattr(op_obj, set_name):
  285. idx = list(getattr(op_obj, set_name)).index(original_opcode)
  286. frozen_set_list[idx] = alt_opcode
  287. new_opcodes_with_args = {}
  288. for opname in opcodes_with_args.keys():
  289. new_opcodes_with_args[opname] = new_opmap[opname]
  290. lowest_opcode_with_arg = min(new_opcodes_with_args.values())
  291. setattr(op_obj, "HAVE_ARGUMENT", lowest_opcode_with_arg)
  292. if hasattr(op_obj, "PJIF"):
  293. if hasattr(op_obj, "POP_JUMP_IF_FALSE") and "POP_JUMP_IF_FALSE" in new_opmap:
  294. # 2.7 and later
  295. setattr(op_obj, "PJIF", new_opmap["POP_JUMP_IF_FALSE"])
  296. if hasattr(op_obj, "JUMP_IF_FALSE") and "JUMP_IF_FALSE" in new_opmap:
  297. setattr(op_obj, "PJIF", new_opmap["JUMP_IF_FALSE"])
  298. if hasattr(op_obj, "PJIT"):
  299. if hasattr(op_obj, "POP_JUMP_IF_TRUE") and "POP_JUMP_IF_TRUE" in new_opmap:
  300. # 2.7 and later
  301. setattr(op_obj, "PJIT", new_opmap["POP_JUMP_IF_TRUE"])
  302. if hasattr(op_obj, "JUMP_IF_TRUE") and "JUMP_IF_TRUE" in new_opmap:
  303. setattr(op_obj, "PJIT", new_opmap["JUMP_IF_TRUE"])
  304. for new_list_name, new_list in new_lists.items():
  305. setattr(op_obj, new_list_name, new_list)
  306. for new_frozenset_name, new_frozenset in new_frozensets.items():
  307. setattr(op_obj, new_frozenset_name, frozenset(new_frozenset))
  308. setattr(op_obj, "opmap", new_opmap)
  309. setattr(op_obj, "REMAPPED", True)
  310. return op_obj
  311. if __name__ == "__main__":
  312. print(get_opcode_module())