opcode_36.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. # (C) Copyright 2016-2017, 2019-2021, 2023-2025
  2. # by Rocky Bernstein
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. """
  18. CPython 3.6 bytecode opcodes
  19. This is like Python 3.6's opcode.py with some classification
  20. of stack usage.
  21. """
  22. from typing import Dict, List, Optional, Tuple
  23. import xdis.opcodes.opcode_35 as opcode_35
  24. from xdis.instruction import Instruction
  25. from xdis.opcodes.base import (
  26. call_op,
  27. def_op,
  28. finalize_opcodes,
  29. init_opdata,
  30. jrel_op,
  31. rm_op,
  32. store_op,
  33. unary_op,
  34. update_pj3,
  35. varargs_op,
  36. )
  37. from xdis.opcodes.format.basic import format_RAISE_VARARGS_older
  38. from xdis.opcodes.format.extended import (
  39. extended_format_ATTR,
  40. extended_format_RAISE_VARARGS_older,
  41. extended_function_signature,
  42. get_arglist,
  43. get_instruction_arg,
  44. )
  45. from xdis.opcodes.opcode_35 import opcode_arg_fmt35, opcode_extended_fmt35
  46. # oppush[op] => number of stack entries pushed
  47. oppush: List[int] = [0] * 256
  48. # oppop[op] => number of stack entries popped
  49. oppop: List[int] = [0] * 256
  50. # When we use EXTENDED_ARG, by how much do we
  51. # shift (or what power of two do we multiply) the operand value?
  52. # Note: this changes in Python 3.6
  53. EXTENDED_ARG_SHIFT = 8
  54. version_tuple = (3, 6)
  55. python_implementation = "CPython"
  56. loc = locals()
  57. init_opdata(loc, opcode_35, version_tuple)
  58. # fmt: off
  59. # These are removed since Python 3.6
  60. rm_op(loc, 'MAKE_CLOSURE', 134)
  61. rm_op(loc, 'CALL_FUNCTION_VAR', 140)
  62. rm_op(loc, 'CALL_FUNCTION_VAR_KW', 142)
  63. # -- Opcodes that have changed drastically --#
  64. # BUILD_MAP_UNPACK_WITH_CALL oparg
  65. # oparg is the number of unpacked mappings which no longer limited by
  66. # 255. As in BUILD_MAP_UNPACK.The location of the function is `oparg +
  67. # 2`.
  68. # CALL_FUNCTION oparg
  69. # oparg is the number of positional arguments on the stack which no
  70. # longer limited by 255.
  71. # CALL_FUNCTION_KW oparg
  72. # This is a different opcode from before, with the name of a similar
  73. # opcode as before. It s like CALL_FUNCTION but values of keyword
  74. # arguments are pushed on the stack after values of positional
  75. # arguments, and then a constant tuple of keyword names is pushed on
  76. # the top of the stack. *oparg* is the sum of numbers of positional
  77. # and keyword arguments. The number of keyword arguments is
  78. # determined by the length of the tuple of keyword names.
  79. # CALL_FUNCTION_EX** takes 2 to 3 arguments on the stack: the
  80. # function, the tuple of positional arguments, and optionally the dict
  81. # of keyword arguments if bit 0 of *oparg* is 1.
  82. # MAKE_FUNCTION oparg
  83. # This is a different opcode from before.
  84. # The tuple of default values for positional-or-keyword parameters,
  85. # the dict of default values for keyword-only parameters, the dict of
  86. # annotations and the closure are pushed on the stack if corresponding
  87. # bit (0-3) is set. They are followed by the code object and the
  88. # qualified name of the function.
  89. # fmt: off
  90. # These are new since Python 3.6
  91. # OP NAME OPCODE POP PUSH
  92. # -----------------------------------------------
  93. def_op(loc, "LOAD_BUILD_CLASS", 71, 0, 1)
  94. store_op(loc, "STORE_ANNOTATION", 127, 1, 0, is_type="name") # Stores TOS index in
  95. # name list;
  96. jrel_op(loc, "SETUP_ASYNC_WITH", 154, 2, 8) # pops __aenter__ and __aexit__;
  97. # pushed results on stack
  98. unary_op(loc, "FORMAT_VALUE", 155, 1, 1)
  99. loc["encoded_arg"].add(155)
  100. varargs_op(loc, "BUILD_CONST_KEY_MAP", 156, -2, 1) # TOS is count of kwargs
  101. call_op(loc, "CALL_FUNCTION_EX", 142, -2, 1)
  102. def_op(loc, "SETUP_ANNOTATIONS", 85, 1, 1)
  103. varargs_op(loc, "BUILD_STRING", 157, -2, 2)
  104. varargs_op(loc, "BUILD_TUPLE_UNPACK_WITH_CALL", 158)
  105. # fmt: on
  106. MAKE_FUNCTION_FLAGS = tuple("default keyword-only annotation closure".split())
  107. FSTRING_CONVERSION_MAP: Dict[int, str] = {0: "", 1: "!s", 2: "!r", 3: "!a"}
  108. def extended_format_BUILD_STRING(
  109. opc, instructions: List[Instruction]
  110. ) -> Tuple[str, Optional[int]]:
  111. assert len(instructions) > 0
  112. arg_count = instructions[0].argval
  113. assert len(instructions) > arg_count
  114. i = 0 # index into instructions
  115. start_offset = instructions[i].offset
  116. str = ""
  117. for _ in range(arg_count):
  118. # Advance to previous instruction and offset
  119. i += 1
  120. start_offset = instructions[i].start_offset
  121. str_part = get_instruction_arg(instructions[i])
  122. if str_part.startswith('f"'):
  123. str_part = str_part[2:-1]
  124. elif str_part.startswith('"'):
  125. str_part = str_part[1:-1]
  126. str += str_part
  127. for j in range(i, len(instructions)):
  128. if instructions[j].offset == start_offset:
  129. i = j
  130. break
  131. else:
  132. return "", None
  133. return 'f"' + str + '"', start_offset
  134. def extended_format_FORMAT_VALUE(
  135. opc, instructions: List[Instruction]
  136. ) -> Tuple[str, Optional[int]]:
  137. inst = instructions[0]
  138. assert len(instructions) > 1
  139. string_value = instructions[1]
  140. start_offset = instructions[1].start_offset
  141. argval = get_instruction_arg(string_value)
  142. s = argval
  143. format_spec = FSTRING_CONVERSION_MAP.get(inst.argval, "")
  144. s = 'f"{%s%s}"' % (format_spec, argval)
  145. return s, start_offset
  146. # Can combine with extended_format_MAKE_FUNCTION_10_27?
  147. def extended_format_MAKE_FUNCTION_36(
  148. opc, instructions: List[Instruction]
  149. ) -> Tuple[str, int]:
  150. assert len(instructions) >= 3
  151. inst = instructions[0]
  152. assert inst.opname in ("MAKE_FUNCTION", "MAKE_CLOSURE")
  153. s = ""
  154. code_inst = instructions[2]
  155. start_offset = code_inst.offset
  156. if code_inst.opname == "LOAD_CONST" and hasattr(code_inst.argval, "co_name"):
  157. arg_flags = instructions[0].argval
  158. param_elision_str = extended_function_signature(code_inst.argval) if arg_flags != 0 else ""
  159. s += (
  160. f"def {code_inst.argval.co_name}({param_elision_str}): ..."
  161. )
  162. return s, start_offset
  163. return s, start_offset
  164. def format_MAKE_FUNCTION_36(flags: int) -> str:
  165. if flags == 0:
  166. return "No arguments"
  167. pattr = ""
  168. for flag in MAKE_FUNCTION_FLAGS:
  169. bit = flags & 1
  170. if bit:
  171. if pattr:
  172. pattr += ", " + flag
  173. else:
  174. pattr = flag
  175. pass
  176. pass
  177. flags >>= 1
  178. return pattr
  179. def format_value_flags(flags) -> str | None:
  180. if (flags & 0x03) == 0x00:
  181. return ""
  182. elif (flags & 0x03) == 0x01:
  183. return "!s"
  184. elif (flags & 0x03) == 0x02:
  185. return "!r"
  186. elif (flags & 0x03) == 0x03:
  187. return "!a"
  188. elif (flags & 0x04) == 0x04:
  189. # pop fmt_spec from the stack and use it, else use an
  190. # empty fmt_spec.
  191. return ""
  192. def format_extended_arg36(arg) -> str:
  193. return str(arg * (1 << 8))
  194. def format_CALL_FUNCTION(argc) -> str:
  195. """argc indicates the number of positional arguments"""
  196. if argc == 1:
  197. plural = ""
  198. else:
  199. plural = "s"
  200. return "%d positional argument%s" % (argc, plural)
  201. def format_CALL_FUNCTION_EX(flags) -> str:
  202. str = ""
  203. if flags & 0x01:
  204. str = "keyword and positional arguments"
  205. else:
  206. str = "positional arguments only"
  207. return str
  208. def format_CALL_FUNCTION_KW(argc):
  209. return "%d total positional and keyword args" % argc
  210. # The meaning of argc changes from 3.5 where this was introduced.
  211. def format_BUILD_MAP_UNPACK_WITH_CALL(count):
  212. """The lowest byte of oparg is the count of mappings, the relative
  213. position of the corresponding callable f is encoded in the second byte
  214. of oparg."""
  215. return "%d mappings" % count
  216. opcode_arg_fmt36 = opcode_arg_fmt = {
  217. "BUILD_MAP_UNPACK_WITH_CALL": format_BUILD_MAP_UNPACK_WITH_CALL,
  218. "CALL_FUNCTION": format_CALL_FUNCTION,
  219. "CALL_FUNCTION_EX": format_CALL_FUNCTION_EX,
  220. "CALL_FUNCTION_KW": format_CALL_FUNCTION_KW,
  221. "EXTENDED_ARG": format_extended_arg36,
  222. "FORMAT_VALUE": format_value_flags,
  223. "MAKE_FUNCTION": format_MAKE_FUNCTION_36,
  224. "RAISE_VARARGS": format_RAISE_VARARGS_older,
  225. }
  226. update_pj3(globals(), loc)
  227. finalize_opcodes(loc)
  228. # Extended formatting routines
  229. # This should be called after updating globals and finalizing opcodes
  230. # since they make use of the information there.
  231. def extended_format_CALL_FUNCTION36(opc, instructions) -> Tuple[str, Optional[int]]:
  232. """call_function_inst should be a "CALL_FUNCTION" instruction. Look in
  233. `instructions` to see if we can find a method name. If not we'll
  234. return None.
  235. """
  236. # From opcode description: arg_count indicates the total number of
  237. # positional and keyword arguments.
  238. call_inst = instructions[0]
  239. arg_count = call_inst.argval
  240. s = ""
  241. arglist, arg_count, i = get_arglist(instructions, 0, arg_count)
  242. if arg_count != 0:
  243. return "", None
  244. assert i is not None
  245. fn_inst = instructions[i + 1]
  246. if arglist is not None and fn_inst.opcode in opc.operator_set:
  247. start_offset = fn_inst.offset
  248. if instructions[1].opname == "MAKE_FUNCTION":
  249. arglist[0] = instructions[2].argval
  250. fn_name = fn_inst.tos_str if fn_inst.tos_str else fn_inst.argrepr
  251. s = f'{fn_name}({", ".join(reversed(arglist))})'
  252. return s, start_offset
  253. return "", None
  254. def extended_format_CALL_FUNCTION_KW(
  255. opc, instructions: List[Instruction]
  256. ) -> Tuple[str, Optional[int]]:
  257. """call_function_inst should be a "CALL_FUNCTION_KW" instruction. Look in
  258. `instructions` to see if we can find a method name. If not we'll
  259. return None.
  260. """
  261. # From opcode description: argc indicates the total number of
  262. # positional and keyword arguments. Sometimes the function name
  263. # is in the stack arg positions back.
  264. # From opcode description: arg_count indicates the total number of
  265. # positional and keyword arguments.
  266. call_inst = instructions[0]
  267. arg_count = call_inst.argval
  268. keywords = instructions[1].argval
  269. s = ""
  270. arglist, arg_count, i = get_arglist(instructions, 1, arg_count)
  271. if arg_count != 0:
  272. return "", None
  273. assert i is not None
  274. if i >= len(instructions) - 1:
  275. return "", None
  276. fn_inst = instructions[i + 1]
  277. start_offset = instructions[i].start_offset
  278. if arglist is not None and fn_inst.opcode in opc.operator_set:
  279. if instructions[1].opname == "MAKE_FUNCTION" and opc.version_tuple >= (3, 3):
  280. arglist[0] = instructions[2].argval
  281. fn_name = fn_inst.tos_str if fn_inst.tos_str else fn_inst.argrepr
  282. # Note, 3.5 and 3.4 and before work slightly different with respect
  283. # to placement of keyword values, and order of arguments.
  284. arglist.reverse()
  285. for i in range(len(keywords)):
  286. j = -(i + 1)
  287. param_name = keywords[j]
  288. arglist[j] = f"{param_name}={arglist[j]}"
  289. str_arglist = ", ".join(arglist)
  290. if len(str_arglist) > 30:
  291. str_arglist = str_arglist[:27] + "..."
  292. s = f"{fn_name}({str_arglist})"
  293. return s, start_offset
  294. return "", None
  295. opcode_arg_fmt = opcode_arg_fmt36 = {
  296. **opcode_arg_fmt35,
  297. **{
  298. "BUILD_MAP_UNPACK_WITH_CALL": format_BUILD_MAP_UNPACK_WITH_CALL,
  299. "CALL_FUNCTION": format_CALL_FUNCTION,
  300. "CALL_FUNCTION_KW": format_CALL_FUNCTION_KW,
  301. "CALL_FUNCTION_EX": format_CALL_FUNCTION_EX,
  302. "MAKE_FUNCTION": format_MAKE_FUNCTION_36,
  303. "FORMAT_VALUE": format_value_flags,
  304. "EXTENDED_ARG": format_extended_arg36,
  305. "RAISE_VARARGS": format_RAISE_VARARGS_older,
  306. },
  307. }
  308. opcode_extended_fmt36 = opcode_extended_fmt = {
  309. **opcode_extended_fmt35,
  310. **{
  311. "BUILD_STRING": extended_format_BUILD_STRING,
  312. "CALL_FUNCTION_KW": extended_format_CALL_FUNCTION_KW,
  313. # "CALL_FUNCTION_VAR": extended_format_CALL_FUNCTION,
  314. "FORMAT_VALUE": extended_format_FORMAT_VALUE,
  315. "MAKE_FUNCTION": extended_format_MAKE_FUNCTION_36,
  316. "RAISE_VARARGS": extended_format_RAISE_VARARGS_older,
  317. "STORE_ATTR": extended_format_ATTR,
  318. },
  319. }