pysource.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  1. # Copyright (c) 2015-2024 by Rocky Bernstein
  2. # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
  3. # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
  4. # Copyright (c) 1999 John Aycock
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """Creates Python source code from an uncompyle6 parse tree.
  19. The terminal symbols are CPython bytecode instructions. (See the
  20. python documentation under module "dis" for a list of instructions
  21. and what they mean).
  22. Upper levels of the grammar is a more-or-less conventional grammar for
  23. Python.
  24. """
  25. # The below is a bit long, but still it is somewhat abbreviated.
  26. # See https://github.com/rocky/python-uncompyle6/wiki/Table-driven-semantic-actions.
  27. # for a more complete explanation, nicely marked up and with examples.
  28. #
  29. #
  30. # Semantic action rules for nonterminal symbols can be specified here by
  31. # creating a method prefaced with "n_" for that nonterminal. For
  32. # example, "n_exec_stmt" handles the semantic actions for the
  33. # "exec_stmt" nonterminal symbol. Similarly if a method with the name
  34. # of the nonterminal is suffixed with "_exit" it will be called after
  35. # all of its children are called.
  36. #
  37. # After a while writing methods this way, you'll find many routines which do similar
  38. # sorts of things, and soon you'll find you want a short notation to
  39. # describe rules and not have to create methods at all.
  40. #
  41. # So another other way to specify a semantic rule for a nonterminal is via
  42. # either tables MAP_R, or MAP_DIRECT where the key is the
  43. # nonterminal name.
  44. #
  45. # These dictionaries use a printf-like syntax to direct substitution
  46. # from attributes of the nonterminal and its children..
  47. #
  48. # The rest of the below describes how table-driven semantic actions work
  49. # and gives a list of the format specifiers. The default() and
  50. # template_engine() methods implement most of the below.
  51. #
  52. # We allow for a couple of ways to interact with a node in a tree. So
  53. # step 1 after not seeing a custom method for a nonterminal is to
  54. # determine from what point of view tree-wise the rule is applied.
  55. # In the diagram below, N is a nonterminal name, and K also a nonterminal
  56. # name but the one used as a key in the table.
  57. # we show where those are with respect to each other in the
  58. # parse tree for N.
  59. #
  60. #
  61. # N&K N
  62. # / | ... \ / | ... \
  63. # O O O O O K
  64. #
  65. #
  66. # TABLE_DIRECT TABLE_R
  67. #
  68. # The default table is TABLE_DIRECT mapping By far, most rules used work this way.
  69. #
  70. # The key K is then extracted from the subtree and used to find one
  71. # of the tables, T listed above. The result after applying T[K] is
  72. # a format string and arguments (a la printf()) for the formatting
  73. # engine.
  74. #
  75. # Escapes in the format string are:
  76. #
  77. # %c evaluate/traverse the node recursively. Its argument is a single
  78. # integer or tuple representing a node index.
  79. # If a tuple is given, the first item is the node index while
  80. # the second item is a string giving the node/noterminal name.
  81. # This name will be checked at runtime against the node type.
  82. #
  83. # %p like %c but sets the operator precedence.
  84. # Its argument then is a tuple indicating the node
  85. # index and the precedence value, an integer. If 3 items are given,
  86. # the second item is the nonterminal name and the precedence is given last.
  87. #
  88. # %C evaluate/travers children recursively, with sibling children separated by the
  89. # given string. It needs a 3-tuple: a starting node, the maximum
  90. # value of an end node, and a string to be inserted between sibling children
  91. #
  92. # %, Append ',' if last %C only printed one item. This is mostly for tuples
  93. # on the LHS of an assignment statement since BUILD_TUPLE_n pretty-prints
  94. # other tuples. The specifier takes no arguments
  95. #
  96. # %P same as %C but sets operator precedence. Its argument is a 4-tuple:
  97. # the node low and high indices, the separator, a string the precedence
  98. # value, an integer.
  99. #
  100. # %D Same as `%C` this is for left-recursive lists like kwargs where goes
  101. # to epsilon at the beginning. It needs a 3-tuple: a starting node, the
  102. # maximum value of an end node, and a string to be inserted between
  103. # sibling children. If we were to use `%C` an extra separator with an
  104. # epsilon would appear at the beginning.
  105. #
  106. # %| Insert spaces to the current indentation level. Takes no arguments.
  107. #
  108. # %+ increase current indentation level. Takes no arguments.
  109. #
  110. # %- decrease current indentation level. Takes no arguments.
  111. #
  112. # %{EXPR} Python eval(EXPR) in context of node. Takes no arguments
  113. #
  114. # %[N]{EXPR} Python eval(EXPR) in context of node[N]. Takes no arguments
  115. #
  116. # %[N]{%X} evaluate/recurse on child node[N], using specifier %X.
  117. # %X can be one of the above, e.g. %c, %p, etc. Takes the arguments
  118. # that the specifier uses.
  119. #
  120. # %% literal '%'. Takes no arguments.
  121. #
  122. #
  123. # The '%' may optionally be followed by a number (C) in square
  124. # brackets, which makes the template_engine walk down to N[C] before
  125. # evaluating the escape code.
  126. import sys
  127. from io import StringIO
  128. from typing import Optional
  129. from spark_parser import GenericASTTraversal
  130. from xdis import COMPILER_FLAG_BIT, IS_PYPY, iscode
  131. from xdis.version_info import PYTHON_VERSION_TRIPLE
  132. from uncompyle6.parser import get_python_parser, parse
  133. from uncompyle6.parsers.treenode import SyntaxTree
  134. from uncompyle6.scanner import Code, get_scanner
  135. from uncompyle6.scanners.tok import Token
  136. from uncompyle6.semantics.check_ast import checker
  137. from uncompyle6.semantics.consts import (
  138. ASSIGN_TUPLE_PARAM,
  139. INDENT_PER_LEVEL,
  140. LINE_LENGTH,
  141. NAME_MODULE,
  142. NO_PARENTHESIS_EVER,
  143. NONE,
  144. PASS,
  145. PRECEDENCE,
  146. RETURN_LOCALS,
  147. RETURN_NONE,
  148. TAB,
  149. TABLE_DIRECT,
  150. TABLE_R,
  151. escape,
  152. )
  153. from uncompyle6.semantics.customize import customize_for_version
  154. from uncompyle6.semantics.gencomp import ComprehensionMixin
  155. from uncompyle6.semantics.helper import (
  156. find_globals_and_nonlocals,
  157. is_lambda_mode,
  158. print_docstring,
  159. )
  160. from uncompyle6.semantics.make_function1 import make_function1
  161. from uncompyle6.semantics.make_function2 import make_function2
  162. from uncompyle6.semantics.make_function3 import make_function3
  163. from uncompyle6.semantics.make_function36 import make_function36
  164. from uncompyle6.semantics.n_actions import NonterminalActions
  165. from uncompyle6.semantics.parser_error import ParserError
  166. from uncompyle6.semantics.transform import TreeTransform, is_docstring
  167. from uncompyle6.show import maybe_show_tree
  168. from uncompyle6.util import better_repr
  169. def unicode(x):
  170. return x
  171. PARSER_DEFAULT_DEBUG = {
  172. "rules": False,
  173. "transition": False,
  174. "reduce": False,
  175. "errorstack": "full",
  176. "context": True,
  177. "dups": False,
  178. }
  179. TREE_DEFAULT_DEBUG = {"before": False, "after": False}
  180. DEFAULT_DEBUG_OPTS = {
  181. "asm": False,
  182. "tree": TREE_DEFAULT_DEBUG,
  183. "grammar": dict(PARSER_DEFAULT_DEBUG),
  184. }
  185. class SourceWalkerError(Exception):
  186. def __init__(self, errmsg):
  187. self.errmsg = errmsg
  188. def __str__(self):
  189. return self.errmsg
  190. class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
  191. """
  192. Class to traverse a Parse Tree of the bytecode instruction built from parsing to
  193. produce some sort of source text.
  194. The Parse tree may be turned an Abstract Syntax tree as an intermediate step.
  195. """
  196. stacked_params = ("f", "indent", "is_lambda", "_globals")
  197. def __init__(
  198. self,
  199. version: tuple,
  200. out,
  201. scanner,
  202. showast=TREE_DEFAULT_DEBUG,
  203. debug_parser=PARSER_DEFAULT_DEBUG,
  204. compile_mode="exec",
  205. is_pypy=IS_PYPY,
  206. linestarts={},
  207. tolerate_errors=False,
  208. ):
  209. """`version' is the Python version of the Python dialect
  210. of both the syntax tree and language we should produce.
  211. `out' is IO-like file pointer to where the output should go. It
  212. would have a getvalue() method.
  213. `scanner' is a method to call when we need to scan tokens. Sometimes
  214. in producing output we will run across further tokens that need
  215. to be scanned.
  216. If `showast' is True, we print the syntax tree.
  217. `compile_mode` is is either `exec`, `single` or `lambda`.
  218. For `lambda`, the grammar that can be used in lambda
  219. expressions is used. Otherwise, it is the compile mode that
  220. was used to create the Syntax Tree and specifies a grammar
  221. variant within a Python version to use.
  222. `is_pypy` should be True if the Syntax Tree was generated for PyPy.
  223. `linestarts` is a dictionary of line number to bytecode offset. This
  224. can sometimes assist in determining which kind of source-code construct
  225. to use when there is ambiguity.
  226. """
  227. GenericASTTraversal.__init__(self, ast=None)
  228. self.scanner = scanner
  229. params = {"f": out, "indent": ""}
  230. self.version = version
  231. self.p = get_python_parser(
  232. version,
  233. debug_parser=dict(debug_parser),
  234. compile_mode=compile_mode,
  235. is_pypy=is_pypy,
  236. )
  237. self.ERROR = None
  238. self.ast_errors = []
  239. self.classes = []
  240. self.compile_mode = compile_mode
  241. self.currentclass = None
  242. self.debug_parser = dict(debug_parser)
  243. self.is_pypy = is_pypy
  244. self.linemap = {}
  245. self.line_number = 1
  246. self.linestarts = linestarts
  247. self.mod_globs = set()
  248. self.name = None
  249. self.offset2inst_index = scanner.offset2inst_index
  250. self.param_stack = []
  251. self.params = params
  252. self.pending_newlines = 0
  253. self.prec = NO_PARENTHESIS_EVER
  254. self.return_none = False
  255. self.showast = showast
  256. self.version = version
  257. self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
  258. # FIXME: have p.insts update in a better way
  259. # modularity is broken here
  260. self.insts = scanner.insts
  261. # Initialize p_lambda on demand
  262. self.p_lambda = None
  263. # This is in Python 2.6 on. It changes the way
  264. # strings get interpreted. See n_LOAD_CONST
  265. self.FUTURE_UNICODE_LITERALS = False
  266. # Sometimes we may want to continue decompiling when there are errors
  267. # and sometimes not
  268. self.tolerate_errors = tolerate_errors
  269. # If we are in a 3.6+ format string, we may need an
  270. # extra level of parens when seeing a lambda. We also use
  271. # this to understand whether or not to add the "f" prefix.
  272. # When not "None" it is a string of the last nonterminal
  273. # that started the format string
  274. self.in_format_string = None
  275. # hide_internal suppresses displaying the additional instructions that sometimes
  276. # exist in code but were not written in the source code.
  277. # An example is:
  278. # __module__ = __name__
  279. self.hide_internal = True
  280. self.TABLE_DIRECT = TABLE_DIRECT.copy()
  281. self.TABLE_R = TABLE_R.copy()
  282. self.MAP_DIRECT = (self.TABLE_DIRECT,)
  283. self.MAP_R = (self.TABLE_R, -1)
  284. self.MAP = {
  285. "stmt": self.MAP_R,
  286. "call": self.MAP_R,
  287. "delete": self.MAP_R,
  288. "store": self.MAP_R,
  289. }
  290. customize_for_version(self, is_pypy, version)
  291. return
  292. def maybe_show_tree(self, tree, phase):
  293. if self.showast.get("before", False):
  294. self.println(
  295. """
  296. ---- end before transform
  297. """
  298. + " "
  299. )
  300. if self.showast.get("after", False):
  301. self.println(
  302. """
  303. ---- begin after transform
  304. """
  305. + " "
  306. )
  307. if self.showast.get(phase, False):
  308. maybe_show_tree(self, tree)
  309. def str_with_template(self, ast):
  310. stream = sys.stdout
  311. stream.write(self.str_with_template1(ast, "", None))
  312. stream.write("\n")
  313. def str_with_template1(self, ast, indent, sibNum=None) -> str:
  314. rv = str(ast.kind)
  315. if sibNum is not None:
  316. rv = "%2d. %s" % (sibNum, rv)
  317. enumerate_children = False
  318. if len(ast) > 1:
  319. rv += f" ({len(ast)})"
  320. enumerate_children = True
  321. if ast in PRECEDENCE:
  322. rv += f", precedence {PRECEDENCE[ast]}"
  323. mapping = self._get_mapping(ast)
  324. table = mapping[0]
  325. key = ast
  326. for i in mapping[1:]:
  327. key = key[i]
  328. pass
  329. if ast.transformed_by is not None:
  330. if ast.transformed_by is True:
  331. rv += " transformed"
  332. else:
  333. rv += " transformed by %s" % ast.transformed_by
  334. pass
  335. pass
  336. if key.kind in table:
  337. rv += ": %s" % str(table[key.kind])
  338. rv = indent + rv
  339. indent += " "
  340. i = 0
  341. for node in ast:
  342. if hasattr(node, "__repr1__"):
  343. if enumerate_children:
  344. child = self.str_with_template1(node, indent, i)
  345. else:
  346. child = self.str_with_template1(node, indent, None)
  347. else:
  348. inst = node.format(line_prefix="L.")
  349. if inst.startswith("\n"):
  350. # Nuke leading \n
  351. inst = inst[1:]
  352. if enumerate_children:
  353. child = indent + "%2d. %s" % (i, inst)
  354. else:
  355. child = indent + inst
  356. pass
  357. rv += "\n" + child
  358. i += 1
  359. return rv
  360. def indent_if_source_nl(self, line_number: int, indent_spaces: str):
  361. if line_number != self.line_number:
  362. self.write("\n" + indent_spaces + INDENT_PER_LEVEL[:-1])
  363. return self.line_number
  364. f = property(
  365. lambda s: s.params["f"],
  366. lambda s, x: s.params.__setitem__("f", x),
  367. lambda s: s.params.__delitem__("f"),
  368. None,
  369. )
  370. indent = property(
  371. lambda s: s.params["indent"],
  372. lambda s, x: s.params.__setitem__("indent", x),
  373. lambda s: s.params.__delitem__("indent"),
  374. None,
  375. )
  376. is_lambda = property(
  377. lambda s: s.params["is_lambda"],
  378. lambda s, x: s.params.__setitem__("is_lambda", x),
  379. lambda s: s.params.__delitem__("is_lambda"),
  380. None,
  381. )
  382. _globals = property(
  383. lambda s: s.params["_globals"],
  384. lambda s, x: s.params.__setitem__("_globals", x),
  385. lambda s: s.params.__delitem__("_globals"),
  386. None,
  387. )
  388. def set_pos_info(self, node):
  389. if hasattr(node, "linestart") and node.linestart:
  390. self.line_number = node.linestart
  391. def preorder(self, node=None):
  392. super(SourceWalker, self).preorder(node)
  393. self.set_pos_info(node)
  394. def indent_more(self, indent=TAB):
  395. self.indent += indent
  396. def indent_less(self, indent=TAB):
  397. self.indent = self.indent[: -len(indent)]
  398. def traverse(self, node, indent=None, is_lambda=False):
  399. self.param_stack.append(self.params)
  400. if indent is None:
  401. indent = self.indent
  402. p = self.pending_newlines
  403. self.pending_newlines = 0
  404. self.params = {
  405. "_globals": {},
  406. "_nonlocals": {}, # Python 3 has nonlocal
  407. "f": StringIO(),
  408. "indent": indent,
  409. "is_lambda": is_lambda,
  410. }
  411. self.preorder(node)
  412. self.f.write("\n" * self.pending_newlines)
  413. result = self.f.getvalue()
  414. self.params = self.param_stack.pop()
  415. self.pending_newlines = p
  416. return result
  417. def write(self, *data):
  418. if (len(data) == 0) or (len(data) == 1 and data[0] == ""):
  419. return
  420. out = "".join((str(j) for j in data))
  421. n = 0
  422. for i in out:
  423. if i == "\n":
  424. n += 1
  425. if n == len(out):
  426. self.pending_newlines = max(self.pending_newlines, n)
  427. return
  428. elif n:
  429. self.pending_newlines = max(self.pending_newlines, n)
  430. out = out[n:]
  431. break
  432. else:
  433. break
  434. if self.pending_newlines > 0:
  435. self.f.write("\n" * self.pending_newlines)
  436. self.pending_newlines = 0
  437. for i in out[::-1]:
  438. if i == "\n":
  439. self.pending_newlines += 1
  440. else:
  441. break
  442. if self.pending_newlines:
  443. out = out[: -self.pending_newlines]
  444. self.f.write(out)
  445. def println(self, *data):
  446. if data and not (len(data) == 1 and data[0] == ""):
  447. self.write(*data)
  448. self.pending_newlines = max(self.pending_newlines, 1)
  449. def is_return_none(self, node):
  450. # Is there a better way?
  451. ret = (
  452. node[0] == "return_expr"
  453. and node[0][0] == "expr"
  454. and node[0][0][0] == "LOAD_CONST"
  455. and node[0][0][0].pattr is None
  456. )
  457. if self.version <= (2, 6):
  458. return ret
  459. else:
  460. # FIXME: should the SyntaxTree expression be folded into
  461. # the global RETURN_NONE constant?
  462. return ret or node == SyntaxTree(
  463. "return", [SyntaxTree("return_expr", [NONE]), Token("RETURN_VALUE")]
  464. )
  465. def pp_tuple(self, tup):
  466. """Pretty print a tuple"""
  467. last_line = self.f.getvalue().split("\n")[-1]
  468. ll = len(last_line) + 1
  469. indent = " " * ll
  470. self.write("(")
  471. sep = ""
  472. for item in tup:
  473. self.write(sep)
  474. ll += len(sep)
  475. s = better_repr(item, self.version)
  476. ll += len(s)
  477. self.write(s)
  478. sep = ","
  479. if ll > LINE_LENGTH:
  480. ll = 0
  481. sep += "\n" + indent
  482. else:
  483. sep += " "
  484. pass
  485. pass
  486. if len(tup) == 1:
  487. self.write(", ")
  488. self.write(")")
  489. # Python changes make function this much that we need at least 3 different routines,
  490. # and probably more in the future.
  491. def make_function(self, node, is_lambda, nested=1, code_node=None, annotate=None):
  492. if self.version <= (1, 2):
  493. make_function1(self, node, is_lambda, nested, code_node)
  494. elif self.version <= (2, 7):
  495. make_function2(self, node, is_lambda, nested, code_node)
  496. elif (3, 0) <= self.version < (3, 6):
  497. make_function3(self, node, is_lambda, nested, code_node)
  498. elif self.version >= (3, 6):
  499. make_function36(self, node, is_lambda, nested, code_node)
  500. def print_super_classes(self, node):
  501. if not (node == "tuple"):
  502. return
  503. n_subclasses = len(node[:-1])
  504. if n_subclasses > 0 or self.version > (2, 4):
  505. # Not an old-style pre-2.2 class
  506. self.write("(")
  507. line_separator = ", "
  508. sep = ""
  509. for elem in node[:-1]:
  510. value = self.traverse(elem)
  511. self.write(sep, value)
  512. sep = line_separator
  513. if n_subclasses > 0 or self.version > (2, 4):
  514. # Not an old-style pre-2.2 class
  515. self.write(")")
  516. def print_super_classes3(self, node):
  517. n = len(node) - 1
  518. j = 0
  519. if node.kind != "expr":
  520. if node == "kwarg":
  521. self.template_engine(("(%[0]{attr}=%c)", 1), node)
  522. return
  523. kwargs = None
  524. assert node[n].kind.startswith("CALL_FUNCTION")
  525. if node[n].kind.startswith("CALL_FUNCTION_KW"):
  526. if self.is_pypy:
  527. # FIXME: this doesn't handle positional and keyword args
  528. # properly. Need to do something more like that below
  529. # in the non-PYPY 3.6 case.
  530. self.template_engine(("(%[0]{attr}=%c)", 1), node[n - 1])
  531. return
  532. else:
  533. kwargs = node[n - 1].attr
  534. assert isinstance(kwargs, tuple)
  535. i = n - (len(kwargs) + 1)
  536. j = 1 + n - node[n].attr
  537. else:
  538. i = start = n - 2
  539. for i in range(start, 0, -1):
  540. if not node[i].kind in ["expr", "call", "LOAD_CLASSNAME"]:
  541. break
  542. pass
  543. if i == start:
  544. return
  545. i += 2
  546. line_separator = ", "
  547. sep = ""
  548. self.write("(")
  549. if kwargs:
  550. # Last arg is tuple of keyword values: omit
  551. m = n - 1
  552. else:
  553. m = n
  554. if kwargs:
  555. # 3.6+ does this
  556. while j < i:
  557. self.write(sep)
  558. value = self.traverse(node[j])
  559. self.write("%s" % value)
  560. sep = line_separator
  561. j += 1
  562. j = 0
  563. while i < m:
  564. self.write(sep)
  565. value = self.traverse(node[i])
  566. self.write("%s=%s" % (kwargs[j], value))
  567. sep = line_separator
  568. j += 1
  569. i += 1
  570. else:
  571. while i < m:
  572. value = self.traverse(node[i])
  573. i += 1
  574. self.write(sep, value)
  575. sep = line_separator
  576. pass
  577. pass
  578. else:
  579. if node[0] == "LOAD_STR":
  580. return
  581. value = self.traverse(node[0])
  582. self.write("(")
  583. self.write(value)
  584. pass
  585. self.write(")")
  586. def kv_map(self, kv_node, sep, line_number, indent):
  587. first_time = True
  588. for kv in kv_node:
  589. assert kv in ("kv", "kv2", "kv3")
  590. # kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR
  591. # kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR
  592. # kv3 ::= expr expr STORE_MAP
  593. # FIXME: DRY this and the above
  594. if kv == "kv":
  595. self.write(sep)
  596. name = self.traverse(kv[-2], indent="")
  597. if first_time:
  598. line_number = self.indent_if_source_nl(line_number, indent)
  599. first_time = False
  600. pass
  601. line_number = self.line_number
  602. self.write(name, ": ")
  603. value = self.traverse(kv[1], indent=self.indent + (len(name) + 2) * " ")
  604. elif kv == "kv2":
  605. self.write(sep)
  606. name = self.traverse(kv[1], indent="")
  607. if first_time:
  608. line_number = self.indent_if_source_nl(line_number, indent)
  609. first_time = False
  610. pass
  611. line_number = self.line_number
  612. self.write(name, ": ")
  613. value = self.traverse(
  614. kv[-3], indent=self.indent + (len(name) + 2) * " "
  615. )
  616. elif kv == "kv3":
  617. self.write(sep)
  618. name = self.traverse(kv[-2], indent="")
  619. if first_time:
  620. line_number = self.indent_if_source_nl(line_number, indent)
  621. first_time = False
  622. pass
  623. line_number = self.line_number
  624. self.write(name, ": ")
  625. line_number = self.line_number
  626. value = self.traverse(kv[0], indent=self.indent + (len(name) + 2) * " ")
  627. pass
  628. self.write(value)
  629. sep = ", "
  630. if line_number != self.line_number:
  631. sep += "\n" + self.indent + " "
  632. line_number = self.line_number
  633. pass
  634. pass
  635. def template_engine(self, entry, startnode):
  636. """The format template interpretation engine. See the comment at the
  637. beginning of this module for how we interpret format
  638. specifications such as %c, %C, and so on.
  639. """
  640. # print("-----")
  641. # print(startnode.kind)
  642. # print(entry[0])
  643. # print('======')
  644. fmt = entry[0]
  645. arg = 1
  646. i = 0
  647. m = escape.search(fmt)
  648. while m:
  649. i = m.end()
  650. self.write(m.group("prefix"))
  651. typ = m.group("type") or "{"
  652. node = startnode
  653. if m.group("child"):
  654. node = node[int(m.group("child"))]
  655. if typ == "%":
  656. self.write("%")
  657. elif typ == "+":
  658. self.line_number += 1
  659. self.indent_more()
  660. elif typ == "-":
  661. self.line_number += 1
  662. self.indent_less()
  663. elif typ == "|":
  664. self.line_number += 1
  665. self.write(self.indent)
  666. # Used mostly on the LHS of an assignment
  667. # BUILD_TUPLE_n is pretty printed and may take care of other uses.
  668. elif typ == ",":
  669. if node.kind in ("unpack", "unpack_w_parens") and node[0].attr == 1:
  670. self.write(",")
  671. elif typ == "c":
  672. index = entry[arg]
  673. if isinstance(index, tuple):
  674. if isinstance(index[1], str):
  675. # if node[index[0]] != index[1]:
  676. # from trepan.api import debug; debug()
  677. assert (
  678. node[index[0]] == index[1]
  679. ), "at %s[%d], expected '%s' node; got '%s'" % (
  680. node.kind,
  681. arg,
  682. index[1],
  683. node[index[0]].kind,
  684. )
  685. else:
  686. assert (
  687. node[index[0]] in index[1]
  688. ), "at %s[%d], expected to be in '%s' node; got '%s'" % (
  689. node.kind,
  690. arg,
  691. index[1],
  692. node[index[0]].kind,
  693. )
  694. index = index[0]
  695. assert isinstance(
  696. index, int
  697. ), "at %s[%d], %s should be int or tuple" % (
  698. node.kind,
  699. arg,
  700. type(index),
  701. )
  702. try:
  703. node[index]
  704. except IndexError:
  705. raise RuntimeError(
  706. f"""
  707. Expanding '{node.kind}' in template '{entry}[{arg}]':
  708. {index} is invalid; has only {len(node)} entries
  709. """
  710. )
  711. self.preorder(node[index])
  712. arg += 1
  713. elif typ == "p":
  714. p = self.prec
  715. # entry[arg]
  716. tup = entry[arg]
  717. assert isinstance(tup, tuple)
  718. if len(tup) == 3:
  719. (index, nonterm_name, self.prec) = tup
  720. if isinstance(tup[1], str):
  721. assert (
  722. node[index] == nonterm_name
  723. ), "at %s[%d], expected '%s' node; got '%s'" % (
  724. node.kind,
  725. arg,
  726. nonterm_name,
  727. node[index].kind,
  728. )
  729. else:
  730. assert node[tup[0]] in tup[1], (
  731. f"at {node.kind}[{tup[0]}], expected to be in '{tup[1]}' "
  732. f"node; got '{node[tup[0]].kind}'"
  733. )
  734. else:
  735. assert len(tup) == 2
  736. (index, self.prec) = entry[arg]
  737. self.preorder(node[index])
  738. self.prec = p
  739. arg += 1
  740. elif typ == "C":
  741. low, high, sep = entry[arg]
  742. remaining = len(node[low:high])
  743. for subnode in node[low:high]:
  744. self.preorder(subnode)
  745. remaining -= 1
  746. if remaining > 0:
  747. self.write(sep)
  748. pass
  749. pass
  750. arg += 1
  751. elif typ == "D":
  752. low, high, sep = entry[arg]
  753. remaining = len(node[low:high])
  754. for subnode in node[low:high]:
  755. remaining -= 1
  756. if len(subnode) > 0:
  757. self.preorder(subnode)
  758. if remaining > 0:
  759. self.write(sep)
  760. pass
  761. pass
  762. pass
  763. arg += 1
  764. elif typ == "x":
  765. # This code is only used in fragments
  766. assert isinstance(entry[arg], tuple)
  767. arg += 1
  768. elif typ == "P":
  769. p = self.prec
  770. low, high, sep, self.prec = entry[arg]
  771. remaining = len(node[low:high])
  772. # remaining = len(node[low:high])
  773. for subnode in node[low:high]:
  774. self.preorder(subnode)
  775. remaining -= 1
  776. if remaining > 0:
  777. self.write(sep)
  778. self.prec = p
  779. arg += 1
  780. elif typ == "{":
  781. expr = m.group("expr")
  782. # Line mapping stuff
  783. if (
  784. hasattr(node, "linestart")
  785. and node.linestart
  786. and hasattr(node, "current_line_number")
  787. ):
  788. self.source_linemap[self.current_line_number] = node.linestart
  789. if expr[0] == "%":
  790. index = entry[arg]
  791. self.template_engine((expr, index), node)
  792. arg += 1
  793. else:
  794. d = node.__dict__
  795. try:
  796. self.write(eval(expr, d, d))
  797. except Exception:
  798. raise
  799. m = escape.search(fmt, i)
  800. self.write(fmt[i:])
  801. def default(self, node):
  802. mapping = self._get_mapping(node)
  803. table = mapping[0]
  804. key = node
  805. for i in mapping[1:]:
  806. key = key[i]
  807. pass
  808. if key.kind in table:
  809. self.template_engine(table[key.kind], node)
  810. self.prune()
  811. def customize(self, customize):
  812. """
  813. Special handling for opcodes, such as those that take a variable number
  814. of arguments -- we add a new entry for each in TABLE_R.
  815. """
  816. for k, v in list(customize.items()):
  817. if k in self.TABLE_R:
  818. continue
  819. op = k[: k.rfind("_")]
  820. if k.startswith("CALL_METHOD"):
  821. # This happens in PyPy and Python 3.7+
  822. self.TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100))
  823. elif self.version >= (3, 6) and k.startswith("CALL_FUNCTION_KW"):
  824. self.TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", 100))
  825. elif op == "CALL_FUNCTION":
  826. self.TABLE_R[k] = (
  827. "%c(%P)",
  828. (0, "expr"),
  829. (1, -1, ", ", PRECEDENCE["yield"] - 1),
  830. )
  831. elif op in (
  832. "CALL_FUNCTION_VAR",
  833. "CALL_FUNCTION_VAR_KW",
  834. "CALL_FUNCTION_KW",
  835. ):
  836. # FIXME: handle everything in customize.
  837. # Right now, some of this is here, and some in that.
  838. if v == 0:
  839. template_str = "%c(%C" # '%C' is a dummy here ...
  840. p2 = (0, 0, None) # because of the None in this
  841. else:
  842. template_str = "%c(%C, "
  843. p2 = (1, -2, ", ")
  844. if op == "CALL_FUNCTION_VAR":
  845. # Python 3.5 only puts optional args (the VAR part)
  846. # the lowest down the stack
  847. if self.version == (3, 5):
  848. if template_str == "%c(%C, ":
  849. entry = ("%c(*%C, %c)", 0, p2, -2)
  850. elif template_str == "%c(%C":
  851. entry = ("%c(*%C)", 0, (1, 100, ""))
  852. elif self.version == (3, 4):
  853. # CALL_FUNCTION_VAR's top element of the stack contains
  854. # the variable argument list
  855. if v == 0:
  856. template_str = "%c(*%c)"
  857. entry = (template_str, 0, -2)
  858. else:
  859. template_str = "%c(%C, *%c)"
  860. entry = (template_str, 0, p2, -2)
  861. else:
  862. template_str += "*%c)"
  863. entry = (template_str, 0, p2, -2)
  864. elif op == "CALL_FUNCTION_KW":
  865. template_str += "**%c)"
  866. entry = (template_str, 0, p2, -2)
  867. elif op == "CALL_FUNCTION_VAR_KW":
  868. template_str += "*%c, **%c)"
  869. # Python 3.5 only puts optional args (the VAR part)
  870. # the lowest down the stack
  871. na = v & 0xFF # positional parameters
  872. if self.version == (3, 5) and na == 0:
  873. if p2[2]:
  874. p2 = (2, -2, ", ")
  875. entry = (template_str, 0, p2, 1, -2)
  876. else:
  877. if p2[2]:
  878. p2 = (1, -3, ", ")
  879. entry = (template_str, 0, p2, -3, -2)
  880. pass
  881. else:
  882. assert False, "Unhandled CALL_FUNCTION %s" % op
  883. self.TABLE_R[k] = entry
  884. pass
  885. # handled by n_dict:
  886. # if op == 'BUILD_SLICE': self.TABLE_R[k] = ('%C' , (0,-1,':'))
  887. # handled by n_list:
  888. # if op == 'BUILD_LIST': self.TABLE_R[k] = ('[%C]' , (0,-1,', '))
  889. # elif op == 'BUILD_TUPLE': self.TABLE_R[k] = ('(%C%,)', (0,-1,', '))
  890. pass
  891. return
  892. # This code is only for Python 1.x - 2.1 ish!
  893. def get_tuple_parameter(self, ast, name):
  894. """
  895. If the name of the formal parameter starts with dot,
  896. it's a tuple parameter, like this:
  897. # def MyFunc(xx, (a,b,c), yy):
  898. # print a, b*2, c*42
  899. In byte-code, the whole tuple is assigned to parameter '.1' and
  900. then the tuple gets unpacked to 'a', 'b' and 'c'.
  901. Since identifiers starting with a dot are illegal in Python,
  902. we can search for the byte-code equivalent to '(a,b,c) = .1'
  903. """
  904. assert ast == "stmts"
  905. for i in range(len(ast)):
  906. # search for an assign-statement
  907. if ast[i] == "sstmt":
  908. node = ast[i][0]
  909. else:
  910. node = ast[i]
  911. if node == "assign" and node[0] == ASSIGN_TUPLE_PARAM(name):
  912. # okay, this assigns '.n' to something
  913. del ast[i]
  914. # walk lhs; this
  915. # returns a tuple of identifiers as used
  916. # within the function definition
  917. assert node[1] == "store"
  918. # if lhs is not a UNPACK_TUPLE (or equiv.),
  919. # add parentheses to make this a tuple
  920. # if node[1][0] not in ('unpack', 'unpack_list'):
  921. result = self.traverse(node[1])
  922. if not (result.startswith("(") and result.endswith(")")):
  923. result = "(%s)" % result
  924. return result
  925. # return self.traverse(node[1])
  926. return f"({name}"
  927. def build_class(self, code):
  928. """Dump class definition, doc string and class body."""
  929. assert iscode(code)
  930. self.classes.append(self.currentclass)
  931. code = Code(code, self.scanner, self.currentclass)
  932. indent = self.indent
  933. # self.println(indent, '#flags:\t', int(code.co_flags))
  934. ast = self.build_ast(code._tokens, code._customize, code)
  935. # save memory by deleting no-longer-used structures
  936. code._tokens = None
  937. if ast[0] == "sstmt":
  938. ast[0] = ast[0][0]
  939. first_stmt = ast[0]
  940. if ast[0] == "docstring":
  941. self.println(self.traverse(ast[0]))
  942. del ast[0]
  943. first_stmt = ast[0]
  944. if (3, 0) <= self.version <= (3, 3):
  945. try:
  946. if first_stmt == "store_locals":
  947. if self.hide_internal:
  948. del ast[0]
  949. if ast[0] == "sstmt":
  950. ast[0] = ast[0][0]
  951. first_stmt = ast[0]
  952. except Exception:
  953. pass
  954. try:
  955. if first_stmt == NAME_MODULE:
  956. if self.hide_internal:
  957. del ast[0]
  958. first_stmt = ast[0]
  959. pass
  960. except Exception:
  961. pass
  962. have_qualname = False
  963. if len(ast):
  964. if ast[0] == "sstmt":
  965. ast[0] = ast[0][0]
  966. first_stmt = ast[0]
  967. if self.version < (3, 0):
  968. # Should we ditch this in favor of the "else" case?
  969. qualname = ".".join(self.classes)
  970. qual_name_tree = SyntaxTree(
  971. "assign",
  972. [
  973. SyntaxTree("expr", [Token("LOAD_CONST", pattr=qualname)]),
  974. SyntaxTree("store", [Token("STORE_NAME", pattr="__qualname__")]),
  975. ],
  976. )
  977. # FIXME: is this right now that we've redone the grammar?
  978. have_qualname = ast[0] == qual_name_tree
  979. else:
  980. # Python 3.4+ has constants like 'cmp_to_key.<locals>.K'
  981. # which are not simple classes like the < 3 case.
  982. try:
  983. if (
  984. first_stmt == "assign"
  985. and first_stmt[0][0] == "LOAD_STR"
  986. and first_stmt[1] == "store"
  987. and first_stmt[1][0] == Token("STORE_NAME", pattr="__qualname__")
  988. ):
  989. have_qualname = True
  990. except Exception:
  991. pass
  992. if have_qualname:
  993. if self.hide_internal:
  994. del ast[0]
  995. pass
  996. # if docstring exists, dump it
  997. if code.co_consts and code.co_consts[0] is not None and len(ast) > 0:
  998. do_doc = False
  999. i = 0
  1000. if is_docstring(ast[0], self.version, code.co_consts):
  1001. do_doc = True
  1002. elif len(ast) > 1 and is_docstring(ast[1], self.version, code.co_consts):
  1003. i = 1
  1004. do_doc = True
  1005. if do_doc and self.hide_internal:
  1006. try:
  1007. # FIXME: Is there an extra [0]?
  1008. docstring = ast[i][0][0][0][0].pattr
  1009. except Exception:
  1010. docstring = code.co_consts[0]
  1011. if print_docstring(self, indent, docstring):
  1012. self.println()
  1013. del ast[i]
  1014. # The function defining a class returns locals() in Python somewhere less than
  1015. # 3.7.
  1016. #
  1017. # We don't want this to show up in the source, so remove the node.
  1018. if len(ast):
  1019. if ast == "stmts" and ast[-1] == "sstmt":
  1020. return_locals_parent = ast[-1]
  1021. parent_index = 0
  1022. else:
  1023. return_locals_parent = ast
  1024. parent_index = -1
  1025. return_locals = return_locals_parent[parent_index]
  1026. if return_locals == RETURN_LOCALS:
  1027. if self.hide_internal:
  1028. del return_locals_parent[parent_index]
  1029. pass
  1030. pass
  1031. # else:
  1032. # print stmt[-1]
  1033. globals, nonlocals = find_globals_and_nonlocals(
  1034. ast, set(), set(), code, self.version
  1035. )
  1036. # Add "global" declaration statements at the top
  1037. # of the function
  1038. for g in sorted(globals):
  1039. self.println(indent, "global ", g)
  1040. for nl in sorted(nonlocals):
  1041. self.println(indent, "nonlocal ", nl)
  1042. old_name = self.name
  1043. self.gen_source(ast, code.co_name, code._customize)
  1044. self.name = old_name
  1045. # save memory by deleting no-longer-used structures
  1046. code._tokens = None
  1047. code._customize = None
  1048. self.classes.pop(-1)
  1049. def gen_source(
  1050. self,
  1051. ast,
  1052. name,
  1053. customize,
  1054. is_lambda=False,
  1055. returnNone=False,
  1056. debug_opts=DEFAULT_DEBUG_OPTS,
  1057. ):
  1058. """convert parse tree to Python source code"""
  1059. rn = self.return_none
  1060. self.return_none = returnNone
  1061. old_name = self.name
  1062. self.name = name
  1063. self.debug_opts = debug_opts
  1064. # if code would be empty, append 'pass'
  1065. if len(ast) == 0:
  1066. self.println(self.indent, "pass")
  1067. else:
  1068. self.customize(customize)
  1069. self.text = self.traverse(ast, is_lambda=is_lambda)
  1070. # In a formatted string using "lambda", we should not add "\n".
  1071. # For example in:
  1072. # f'{(lambda x:x)("8")!r}'
  1073. # Adding a "\n" after "lambda x: x" will give an error message:
  1074. # SyntaxError: f-string expression part cannot include a backslash
  1075. # So avoid \n after writing text
  1076. self.write(self.text)
  1077. self.name = old_name
  1078. self.return_none = rn
  1079. def build_ast(
  1080. self,
  1081. tokens,
  1082. customize,
  1083. code,
  1084. is_lambda=False,
  1085. noneInNames=False,
  1086. is_top_level_module=False,
  1087. compile_mode="exec",
  1088. ) -> GenericASTTraversal:
  1089. # FIXME: DRY with fragments.py
  1090. # assert isinstance(tokens[0], Token)
  1091. if is_lambda:
  1092. for t in tokens:
  1093. if t.kind == "RETURN_END_IF":
  1094. t.kind = "RETURN_END_IF_LAMBDA"
  1095. elif t.kind == "RETURN_VALUE":
  1096. t.kind = "RETURN_VALUE_LAMBDA"
  1097. tokens.append(Token("LAMBDA_MARKER"))
  1098. try:
  1099. # FIXME: have p.insts update in a better way
  1100. # modularity is broken here
  1101. p_insts = self.p.insts
  1102. self.p.insts = self.scanner.insts
  1103. self.p.offset2inst_index = self.scanner.offset2inst_index
  1104. ast = parse(self.p, tokens, customize, code)
  1105. self.customize(customize)
  1106. self.p.insts = p_insts
  1107. except (ParserError, AssertionError) as e:
  1108. raise ParserError(e, tokens, self.p.debug["reduce"])
  1109. transform_tree = self.treeTransform.transform(ast, code)
  1110. self.maybe_show_tree(ast, phase="after")
  1111. del ast # Save memory
  1112. return transform_tree
  1113. # The bytecode for the end of the main routine has a "return
  1114. # None". However, you can't issue a "return" statement in
  1115. # main. So as the old cigarette slogan goes: I'd rather switch
  1116. # (the token stream) than fight (with the grammar to not emit
  1117. # "return None").
  1118. if self.hide_internal:
  1119. if len(tokens) >= 2 and not noneInNames:
  1120. if tokens[-1].kind in ("RETURN_VALUE", "RETURN_VALUE_LAMBDA"):
  1121. # Python 3.4's classes can add a "return None" which is
  1122. # invalid syntax.
  1123. load_const = tokens[-2]
  1124. # We should have:
  1125. # LOAD_CONST None
  1126. # with *no* line number associated the token.
  1127. # A line number on the token or a non-None
  1128. # token value a token based on user source
  1129. # text.
  1130. if (
  1131. load_const.kind == "LOAD_CONST"
  1132. and load_const.linestart is None
  1133. and load_const.attr is None
  1134. ):
  1135. # Delete LOAD_CONST (None) RETURN_VALUE
  1136. del tokens[-2:]
  1137. else:
  1138. tokens.append(Token("RETURN_LAST"))
  1139. if len(tokens) == 0:
  1140. return PASS
  1141. # Build a parse tree from a tokenized and massaged disassembly.
  1142. try:
  1143. # FIXME: have p.insts update in a better way
  1144. # Modularity is broken here.
  1145. p_insts = self.p.insts
  1146. self.p.insts = self.scanner.insts
  1147. self.p.offset2inst_index = self.scanner.offset2inst_index
  1148. self.p.opc = self.scanner.opc
  1149. ast = parse(self.p, tokens, customize, code)
  1150. self.p.insts = p_insts
  1151. except (ParserError, AssertionError) as e:
  1152. raise ParserError(e, tokens, self.p.debug["reduce"])
  1153. checker(ast, False, self.ast_errors)
  1154. self.customize(customize)
  1155. transform_tree = self.treeTransform.transform(ast, code)
  1156. self.maybe_show_tree(transform_tree, phase="after")
  1157. del ast # Save memory
  1158. return transform_tree
  1159. def _get_mapping(self, node):
  1160. return self.MAP.get(node, self.MAP_DIRECT)
  1161. def code_deparse(
  1162. co,
  1163. out=sys.stdout,
  1164. version: Optional[tuple] = None,
  1165. debug_opts=DEFAULT_DEBUG_OPTS,
  1166. code_objects={},
  1167. compile_mode="exec",
  1168. is_pypy=IS_PYPY,
  1169. walker=SourceWalker,
  1170. start_offset: int = 0,
  1171. stop_offset: int = -1,
  1172. ) -> Optional[SourceWalker]:
  1173. """
  1174. ingests and deparses a given code block 'co'. If version is None,
  1175. we will use the current Python interpreter version.
  1176. """
  1177. assert iscode(co)
  1178. if out is None:
  1179. out = sys.stdout
  1180. if version is None:
  1181. version = PYTHON_VERSION_TRIPLE
  1182. # store final output stream for case of error
  1183. scanner = get_scanner(version, is_pypy=is_pypy, show_asm=debug_opts["asm"])
  1184. tokens, customize = scanner.ingest(
  1185. co, code_objects=code_objects, show_asm=debug_opts["asm"]
  1186. )
  1187. if start_offset > 0:
  1188. for i, t in enumerate(tokens):
  1189. # If t.offset is a string, we want to skip this.
  1190. if isinstance(t.offset, int) and t.offset >= start_offset:
  1191. tokens = tokens[i:]
  1192. break
  1193. if stop_offset > -1:
  1194. for i, t in enumerate(tokens):
  1195. # In contrast to the test for start_offset If t.offset is
  1196. # a string, we want to extract the integer offset value.
  1197. if t.off2int() >= stop_offset:
  1198. tokens = tokens[:i]
  1199. break
  1200. debug_parser = debug_opts.get("grammar", dict(PARSER_DEFAULT_DEBUG))
  1201. # Build Syntax Tree from disassembly.
  1202. linestarts = dict(scanner.opc.findlinestarts(co))
  1203. deparsed = walker(
  1204. version,
  1205. out,
  1206. scanner,
  1207. showast=debug_opts.get("tree", TREE_DEFAULT_DEBUG),
  1208. debug_parser=debug_parser,
  1209. compile_mode=compile_mode,
  1210. is_pypy=is_pypy,
  1211. linestarts=linestarts,
  1212. )
  1213. is_top_level_module = co.co_name == "<module>"
  1214. if compile_mode == "eval":
  1215. deparsed.hide_internal = False
  1216. deparsed.compile_mode = compile_mode
  1217. deparsed.ast = deparsed.build_ast(
  1218. tokens,
  1219. customize,
  1220. co,
  1221. is_lambda=is_lambda_mode(compile_mode),
  1222. is_top_level_module=is_top_level_module,
  1223. compile_mode=compile_mode,
  1224. )
  1225. # XXX workaround for profiling
  1226. if deparsed.ast is None:
  1227. return None
  1228. # FIXME use a lookup table here.
  1229. if is_lambda_mode(compile_mode):
  1230. expected_start = "lambda_start"
  1231. elif compile_mode == "eval":
  1232. expected_start = "expr_start"
  1233. elif compile_mode == "expr":
  1234. expected_start = "expr_start"
  1235. elif compile_mode == "exec":
  1236. expected_start = "stmts"
  1237. elif compile_mode == "single":
  1238. # expected_start = "single_start"
  1239. expected_start = None
  1240. else:
  1241. expected_start = None
  1242. if expected_start:
  1243. assert deparsed.ast == expected_start, (
  1244. f"Should have parsed grammar start to '{expected_start}'; "
  1245. f"got: {deparsed.ast.kind}"
  1246. )
  1247. # save memory
  1248. del tokens
  1249. deparsed.mod_globs, nonlocals = find_globals_and_nonlocals(
  1250. deparsed.ast, set(), set(), co, version
  1251. )
  1252. assert not nonlocals
  1253. # convert leading '__doc__ = "..." into doc string
  1254. try:
  1255. stmts = deparsed.ast
  1256. first_stmt = stmts[0]
  1257. if version >= (3, 6):
  1258. if first_stmt[0] == "SETUP_ANNOTATIONS":
  1259. del stmts[0]
  1260. assert stmts[0] == "sstmt"
  1261. # Nuke sstmt
  1262. first_stmt = stmts[0][0]
  1263. pass
  1264. pass
  1265. if first_stmt == "docstring":
  1266. print_docstring(deparsed, "", co.co_consts[0])
  1267. del stmts[0]
  1268. if stmts[-1] == RETURN_NONE:
  1269. stmts.pop() # remove last node
  1270. # todo: if empty, add 'pass'
  1271. except Exception:
  1272. pass
  1273. deparsed.FUTURE_UNICODE_LITERALS = (
  1274. COMPILER_FLAG_BIT["FUTURE_UNICODE_LITERALS"] & co.co_flags != 0
  1275. )
  1276. # What we've been waiting for: Generate source from Syntax Tree!
  1277. deparsed.gen_source(
  1278. deparsed.ast,
  1279. name=co.co_name,
  1280. customize=customize,
  1281. is_lambda=is_lambda_mode(compile_mode),
  1282. debug_opts=debug_opts,
  1283. )
  1284. for g in sorted(deparsed.mod_globs):
  1285. deparsed.write("# global %s ## Warning: Unused global\n" % g)
  1286. if deparsed.ast_errors:
  1287. deparsed.write("# NOTE: have internal decompilation grammar errors.\n")
  1288. deparsed.write("# Use -T option to show full context.")
  1289. for err in deparsed.ast_errors:
  1290. deparsed.write(err)
  1291. raise SourceWalkerError("Deparsing hit an internal grammar-rule bug")
  1292. if deparsed.ERROR:
  1293. raise SourceWalkerError("Deparsing stopped due to parse error")
  1294. return deparsed
  1295. def deparse_code2str(
  1296. code,
  1297. out=sys.stdout,
  1298. version=None,
  1299. debug_opts=DEFAULT_DEBUG_OPTS,
  1300. code_objects={},
  1301. compile_mode="exec",
  1302. is_pypy=IS_PYPY,
  1303. walker=SourceWalker,
  1304. start_offset: int = 0,
  1305. stop_offset: int = -1,
  1306. ) -> str:
  1307. """
  1308. Return the deparsed text for a Python code object. `out` is where
  1309. any intermediate output for assembly or tree output will be sent.
  1310. """
  1311. return code_deparse(
  1312. code,
  1313. out,
  1314. version,
  1315. debug_opts,
  1316. code_objects=code_objects,
  1317. compile_mode=compile_mode,
  1318. is_pypy=is_pypy,
  1319. walker=walker,
  1320. ).text
  1321. if __name__ == "__main__":
  1322. def deparse_test(co):
  1323. """This is a docstring"""
  1324. s = deparse_code2str(co)
  1325. # s = deparse_code2str(co, debug_opts={"asm": "after", "tree": {'before': False, 'after': False}})
  1326. print(s)
  1327. return
  1328. deparse_test(deparse_test.__code__)