make_function36.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. # Copyright (c) 2019-2022 by Rocky Bernstein
  2. #
  3. # This program is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 3 of the License, or
  6. # (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, see <http://www.gnu.org/licenses/>.
  15. """
  16. All the crazy things we have to do to handle Python functions.
  17. """
  18. from xdis import (
  19. CO_ASYNC_GENERATOR,
  20. CO_GENERATOR,
  21. code_has_star_arg,
  22. code_has_star_star_arg,
  23. iscode,
  24. )
  25. from decompyle3.parsers.parse_heads import ParserError as ParserError2
  26. from decompyle3.scanner import Code
  27. from decompyle3.semantics.helper import (
  28. find_all_globals,
  29. find_globals_and_nonlocals,
  30. find_none,
  31. )
  32. from decompyle3.semantics.parser_error import ParserError
  33. from decompyle3.show import maybe_show_tree_param_default
  34. def make_function36(self, node, is_lambda, nested=1, code_node=None):
  35. """Dump function definition, doc string, and function body in
  36. Python version 3.6 and above.
  37. """
  38. # MAKE_CLOSURE adds a closure slot
  39. # In Python 3.6 and above stack change again. I understand
  40. # 3.7 changes some of those changes, although I don't
  41. # see it in this code yet. Yes, it is hard to follow,
  42. # and I am sure I haven't been able to keep up.
  43. # Thank you, Python.
  44. def build_param(ast, name, default, annotation=None):
  45. """build parameters:
  46. - handle defaults
  47. - handle format tuple parameters
  48. """
  49. value = default
  50. maybe_show_tree_param_default(self.showast, name, value)
  51. if annotation:
  52. result = "%s: %s=%s" % (name, annotation, value)
  53. else:
  54. result = "%s=%s" % (name, value)
  55. # The below can probably be removed. This is probably
  56. # a holdover from days when LOAD_CONST erroneously
  57. # didn't handle LOAD_CONST None properly
  58. if result[-2:] == "= ": # default was 'LOAD_CONST None'
  59. result += "None"
  60. return result
  61. # MAKE_FUNCTION_... or MAKE_CLOSURE_...
  62. assert node[-1].kind.startswith("MAKE_")
  63. # Python 3.3+ adds a qualified name at TOS (-1)
  64. # moving down the LOAD_LAMBDA instruction
  65. lambda_index = -3
  66. args_node = node[-1]
  67. annotate_dict = {}
  68. # Get a list of tree nodes that constitute the values for the "default
  69. # parameters"; these are default values that appear before any *, and are
  70. # not to be confused with keyword parameters which may appear after *.
  71. args_attr = args_node.attr
  72. if len(args_attr) == 3:
  73. _, kw_args, annotate_argc = args_attr
  74. else:
  75. _, kw_args, annotate_argc, closure = args_attr
  76. i = -4 if node[-2] != "docstring" else -5
  77. if annotate_argc:
  78. # Turn into subroutine and DRY with other use
  79. annotate_node = node[i]
  80. if annotate_node == "expr":
  81. annotate_node = annotate_node[0]
  82. annotate_name_node = annotate_node[-1]
  83. if annotate_node == "dict" and annotate_name_node.kind.startswith(
  84. "BUILD_CONST_KEY_MAP"
  85. ):
  86. types = [self.traverse(n, indent="") for n in annotate_node[:-2]]
  87. names = annotate_node[-2].attr
  88. length = len(types)
  89. assert length == len(names)
  90. for i in range(length):
  91. annotate_dict[names[i]] = types[i]
  92. pass
  93. pass
  94. i -= 1
  95. if closure:
  96. # FIXME: fill in
  97. # annotate = node[i]
  98. i -= 1
  99. defparams = []
  100. # FIXME: DRY with code below
  101. default, kw_args, annotate_argc = args_node.attr[0:3]
  102. if default:
  103. expr_node = node[0]
  104. if node[0] == "pos_arg":
  105. expr_node = expr_node[0]
  106. assert expr_node == "expr", "expecting mkfunc default node to be an expr"
  107. if expr_node[0] == "LOAD_CONST" and isinstance(expr_node[0].attr, tuple):
  108. defparams = [repr(a) for a in expr_node[0].attr]
  109. elif expr_node[0] in frozenset(("list", "tuple", "dict", "set")):
  110. defparams = [self.traverse(n, indent="") for n in expr_node[0][:-1]]
  111. else:
  112. defparams = []
  113. pass
  114. if lambda_index and is_lambda and iscode(node[lambda_index].attr):
  115. assert node[lambda_index].kind == "LOAD_LAMBDA"
  116. code = node[lambda_index].attr
  117. else:
  118. code = code_node.attr
  119. assert iscode(code)
  120. debug_asm_opts = self.debug_opts["asm"] if self.debug_opts else None
  121. scanner_code = Code(code, self.scanner, self.currentclass, debug_asm_opts)
  122. # add defaults values to parameter names
  123. argc = code.co_argcount
  124. kwonlyargcount = code.co_kwonlyargcount
  125. paramnames = list(scanner_code.co_varnames[:argc])
  126. kwargs = list(scanner_code.co_varnames[argc: argc + kwonlyargcount])
  127. paramnames.reverse()
  128. defparams.reverse()
  129. try:
  130. tree = self.build_ast(
  131. scanner_code._tokens,
  132. scanner_code._customize,
  133. scanner_code,
  134. is_lambda=is_lambda,
  135. noneInNames=("None" in code.co_names),
  136. )
  137. except (ParserError, ParserError2) as p:
  138. self.write(str(p))
  139. if not self.tolerate_errors:
  140. self.ERROR = p
  141. return
  142. i = len(paramnames) - len(defparams)
  143. # build parameters
  144. params = []
  145. if defparams:
  146. for i, defparam in enumerate(defparams):
  147. params.append(
  148. build_param(
  149. tree, paramnames[i], defparam, annotate_dict.get(paramnames[i])
  150. )
  151. )
  152. for param in paramnames[i + 1:]:
  153. if param in annotate_dict:
  154. params.append("%s: %s" % (param, annotate_dict[param]))
  155. else:
  156. params.append(param)
  157. else:
  158. for param in paramnames:
  159. if param in annotate_dict:
  160. params.append("%s: %s" % (param, annotate_dict[param]))
  161. else:
  162. params.append(param)
  163. params.reverse() # back to correct order
  164. if code_has_star_arg(code):
  165. star_arg = code.co_varnames[argc + kwonlyargcount]
  166. if star_arg in annotate_dict:
  167. params.append("*%s: %s" % (star_arg, annotate_dict[star_arg]))
  168. else:
  169. params.append("*%s" % star_arg)
  170. argc += 1
  171. # dump parameter list (with default values)
  172. if is_lambda:
  173. self.write("lambda")
  174. if len(params):
  175. self.write(" ", ", ".join(params))
  176. elif kwonlyargcount > 0 and not (4 & code.co_flags):
  177. assert argc == 0
  178. self.write(" ")
  179. # If the last statement is None (which is the
  180. # same thing as "return None" in a lambda) and the
  181. # next to last statement is a "yield". Then we want to
  182. # drop the (return) None since that was just put there
  183. # to have something to after the yield finishes.
  184. # FIXME: this is a bit hoaky and not general
  185. if (
  186. len(tree) > 1
  187. and self.traverse(tree[-1]) == "None"
  188. and self.traverse(tree[-2]).strip().startswith("yield")
  189. ):
  190. del tree[-1]
  191. # Now pick out the expr part of the last statement
  192. tree_expr = tree[-1]
  193. while tree_expr.kind != "expr":
  194. tree_expr = tree_expr[0]
  195. tree[-1] = tree_expr
  196. pass
  197. else:
  198. self.write("(", ", ".join(params))
  199. # self.println(indent, '#flags:\t', int(code.co_flags))
  200. ends_in_comma = False
  201. if kwonlyargcount > 0:
  202. if not 4 & code.co_flags:
  203. if argc > 0:
  204. self.write(", *, ")
  205. else:
  206. self.write("*, ")
  207. pass
  208. else:
  209. if argc > 0:
  210. self.write(", ")
  211. # ann_dict = kw_dict = default_tup = None
  212. kw_dict = None
  213. fn_bits = node[-1].attr
  214. # Skip over:
  215. # MAKE_FUNCTION,
  216. # optional docstring
  217. # LOAD_CONST qualified name,
  218. # LOAD_CONST code object
  219. index = -5 if node[-2] == "docstring" else -4
  220. if fn_bits[-1]:
  221. index -= 1
  222. if fn_bits[-2]:
  223. # ann_dict = node[index]
  224. index -= 1
  225. if fn_bits[-3]:
  226. kw_dict = node[index]
  227. index -= 1
  228. if fn_bits[-4]:
  229. # default_tup = node[index]
  230. pass
  231. if kw_dict == "expr":
  232. kw_dict = kw_dict[0]
  233. kw_args = [None] * kwonlyargcount
  234. # FIXME: handle free_tup, ann_dict, and default_tup
  235. if kw_dict:
  236. assert kw_dict == "dict"
  237. const_list = kw_dict[0]
  238. if kw_dict[0] == "const_list":
  239. add_consts = const_list[1]
  240. assert add_consts == "add_consts"
  241. names = add_consts[-1].attr
  242. defaults = [v.pattr for v in add_consts[:-1]]
  243. else:
  244. defaults = [self.traverse(n, indent="") for n in kw_dict[:-2]]
  245. names = eval(self.traverse(kw_dict[-2]))
  246. assert len(defaults) == len(names)
  247. # FIXME: possibly handle line breaks
  248. for i, n in enumerate(names):
  249. idx = kwargs.index(n)
  250. if annotate_dict and n in annotate_dict:
  251. t = "%s: %s=%s" % (n, annotate_dict[n], defaults[i])
  252. else:
  253. t = "%s=%s" % (n, defaults[i])
  254. kw_args[idx] = t
  255. pass
  256. pass
  257. # handle others
  258. other_kw = [c is None for c in kw_args]
  259. for i, flag in enumerate(other_kw):
  260. if flag:
  261. n = kwargs[i]
  262. if n in annotate_dict:
  263. kw_args[i] = "%s: %s" % (n, annotate_dict[n])
  264. else:
  265. kw_args[i] = "%s" % n
  266. self.write(", ".join(kw_args))
  267. ends_in_comma = False
  268. pass
  269. else:
  270. if argc == 0:
  271. ends_in_comma = True
  272. if code_has_star_star_arg(code):
  273. if not ends_in_comma:
  274. self.write(", ")
  275. star_star_arg = code.co_varnames[argc + kwonlyargcount]
  276. if annotate_dict and star_star_arg in annotate_dict:
  277. self.write("**%s: %s" % (star_star_arg, annotate_dict[star_star_arg]))
  278. else:
  279. self.write("**%s" % star_star_arg)
  280. if is_lambda:
  281. self.write(": ")
  282. else:
  283. self.write(")")
  284. if annotate_dict and "return" in annotate_dict:
  285. self.write(" -> %s" % annotate_dict["return"])
  286. self.println(":")
  287. if node[-2] == "docstring" and not is_lambda:
  288. # docstring exists, dump it
  289. self.println(self.traverse(node[-2]))
  290. assert tree in ("stmts", "lambda_start")
  291. all_globals = find_all_globals(tree, set())
  292. globals, nonlocals = find_globals_and_nonlocals(
  293. tree, set(), set(), code, self.version
  294. )
  295. for g in sorted((all_globals & self.mod_globs) | globals):
  296. self.println(self.indent, "global ", g)
  297. for nl in sorted(nonlocals):
  298. self.println(self.indent, "nonlocal ", nl)
  299. self.mod_globs -= all_globals
  300. has_none = "None" in code.co_names
  301. rn = has_none and not find_none(tree)
  302. self.gen_source(
  303. tree,
  304. code.co_name,
  305. scanner_code._customize,
  306. is_lambda=is_lambda,
  307. returnNone=rn,
  308. debug_opts=self.debug_opts,
  309. )
  310. # In obscure cases, a function may be a generator but the "yield"
  311. # was optimized away. Here, we need to put in unreachable code to
  312. # add in "yield" just so that the compiler will mark
  313. # the GENERATOR bit of the function. See for example
  314. # Python 3.x's test_connection.py and test_contexlib_async test programs.
  315. if not is_lambda and code.co_flags & (CO_GENERATOR | CO_ASYNC_GENERATOR):
  316. need_bogus_yield = True
  317. for token in scanner_code._tokens:
  318. if token == "YIELD_VALUE":
  319. need_bogus_yield = False
  320. break
  321. pass
  322. if need_bogus_yield:
  323. self.template_engine(("%|if False:\n%+%|yield None%-",), node)
  324. scanner_code._tokens = None # save memory
  325. scanner_code._customize = None # save memory