fragments.py 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224
  1. # Copyright (c) 2015-2024 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. Creates Python source code from an decompyle3 parse tree,
  17. and indexes fragments which can be accessed by instruction offset
  18. address.
  19. See https://github.com/rocky/python-decompyle3/wiki/Table-driven-semantic-actions.
  20. for a more complete explanation, nicely marked up and with examples.
  21. We add some format specifiers here not used in pysource
  22. 1. %x
  23. -----
  24. %x takes an argument (src, (dest...)) and copies all of the range attributes
  25. from src to all nodes under dest.
  26. For example in:
  27. 'import': ( '%|import %c%x\n', 2, (2,(0,1)), ),
  28. node 2 range information, it in %c, is copied to nodes 0 and 1. If
  29. 1. is a nonterminal, all the nodes under it get node2 range information.
  30. 2. %r
  31. -----
  32. %r associates recursively location information for the string that follows
  33. For example in:
  34. 'break': ( '%|%rbreak\n', ),
  35. The node will be associated with the text break, excluding the trailing newline.
  36. Note we associate the accumulated text with the node normally, but we just don't
  37. do it recursively which is where offsets are probably located.
  38. 2. %b
  39. -----
  40. %b associates the text from the specified index to what we have now.
  41. it takes an integer argument.
  42. For example in:
  43. 'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
  44. The node position 0 will be associated with "import".
  45. """
  46. # FIXME: DRY code with pysource
  47. import re
  48. from bisect import bisect_right
  49. from collections import namedtuple
  50. from typing import Optional
  51. from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG, GenericASTTraversal
  52. from spark_parser.ast import GenericASTTraversalPruningException
  53. from xdis import iscode
  54. from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
  55. import decompyle3.parsers.main as python_parser
  56. import decompyle3.parsers.parse_heads as heads
  57. from decompyle3.parsers.main import get_python_parser
  58. from decompyle3.parsers.treenode import SyntaxTree
  59. from decompyle3.scanner import Code, Token, get_scanner
  60. from decompyle3.semantics import pysource
  61. from decompyle3.semantics.check_ast import checker
  62. from decompyle3.semantics.consts import (
  63. INDENT_PER_LEVEL,
  64. MAP,
  65. NONE,
  66. PASS,
  67. PRECEDENCE,
  68. TABLE_DIRECT,
  69. escape,
  70. )
  71. from decompyle3.semantics.make_function36 import make_function36
  72. from decompyle3.semantics.pysource import (
  73. DEFAULT_DEBUG_OPTS,
  74. TREE_DEFAULT_DEBUG,
  75. ParserError,
  76. StringIO,
  77. )
  78. from decompyle3.show import maybe_show_tree
  79. NodeInfo = namedtuple("NodeInfo", "node start finish")
  80. ExtractInfo = namedtuple(
  81. "ExtractInfo",
  82. "lineNo lineStartOffset markerLine selectedLine selectedText nonterminal",
  83. )
  84. TABLE_DIRECT_FRAGMENT = {
  85. "break": ("%|%rbreak\n",),
  86. "continue ": ("%|%rcontinue\n",),
  87. "pass": ("%|%rpass\n",),
  88. "raise_stmt0": ("%|%rraise\n",),
  89. "import": ("%|import %c%x\n", 2, (2, (0, 1))),
  90. "import_cont": (", %c%x", (2, "alias"), (2, (0, 1))),
  91. "import_from": ("%|from %[2]{pattr}%x import %c\n", (2, (0, 1)), (3, "importlist")),
  92. "importfrom": ("%|from %[2]{pattr}%x import %c\n", (2, (0, 1)), 3),
  93. # FIXME only in <= 2.4
  94. "importmultiple": ("%|import%b %c%c\n", 0, 2, 3),
  95. "list_for": (" for %c%x in %c%c", 2, (2, (1,)), 0, 3),
  96. "for": ("%|for%b %c%x in %c:\n%+%c%-\n\n", 0, (3, "store"), (3, (2,)), 1, 4),
  97. "forelsestmt": (
  98. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  99. 0,
  100. (3, "store"),
  101. (3, (2,)),
  102. 1,
  103. 4,
  104. -2,
  105. ),
  106. "forelselaststmt": (
  107. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-",
  108. 0,
  109. (3, "store"),
  110. (3, (2,)),
  111. 1,
  112. 4,
  113. -2,
  114. ),
  115. "forelselaststmtc": (
  116. "%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
  117. 0,
  118. (3, "store"),
  119. (3, (2,)),
  120. 1,
  121. 4,
  122. -2,
  123. ),
  124. "whilestmt": ("%|while%b %c:\n%+%c%-\n\n", 0, 1, 2),
  125. "whileelsestmt": ("%|while%b %c:\n%+%c%-%|else:\n%+%c%-\n\n", 0, 1, 2, -2),
  126. "whileelselaststmt": ("%|while%b %c:\n%+%c%-%|else:\n%+%c%-", 0, 1, 2, -2),
  127. }
  128. class FragmentsWalker(pysource.SourceWalker, object):
  129. MAP_DIRECT_FRAGMENT = ()
  130. stacked_params = ("f", "indent", "is_lambda", "_globals")
  131. def __init__(
  132. self,
  133. version: tuple,
  134. scanner,
  135. showast=TREE_DEFAULT_DEBUG,
  136. debug_parser=PARSER_DEFAULT_DEBUG,
  137. compile_mode="exec",
  138. is_pypy=IS_PYPY,
  139. linestarts={},
  140. tolerate_errors=True,
  141. ):
  142. pysource.SourceWalker.__init__(
  143. self,
  144. version=version,
  145. out=StringIO(),
  146. scanner=scanner,
  147. showast=showast,
  148. debug_parser=debug_parser,
  149. compile_mode=compile_mode,
  150. is_pypy=is_pypy,
  151. linestarts=linestarts,
  152. tolerate_errors=tolerate_errors,
  153. )
  154. # hide_internal suppresses displaying the additional instructions that sometimes
  155. # exist in code but were not written in the source code.
  156. # An example is:
  157. # __module__ = __name__
  158. # If showing source code we generally don't want to show this. However in fragment
  159. # deparsing we generally do need to see these instructions since we may be stopped
  160. # at one. So here we do not want to suppress showing such instructions.
  161. self.hide_internal = False
  162. self.offsets = {}
  163. self.last_finish = -1
  164. self.is_pypy = is_pypy
  165. # FIXME: is there a better way?
  166. self.TABLE_DIRECT = TABLE_DIRECT.copy()
  167. self.MAP_DIRECT_FRAGMENT = (dict(self.TABLE_DIRECT, **TABLE_DIRECT_FRAGMENT),)
  168. return
  169. f = property(
  170. lambda s: s.params["f"],
  171. lambda s, x: s.params.__setitem__("f", x),
  172. lambda s: s.params.__delitem__("f"),
  173. None,
  174. )
  175. indent = property(
  176. lambda s: s.params["indent"],
  177. lambda s, x: s.params.__setitem__("indent", x),
  178. lambda s: s.params.__delitem__("indent"),
  179. None,
  180. )
  181. is_lambda = property(
  182. lambda s: s.params["is_lambda"],
  183. lambda s, x: s.params.__setitem__("is_lambda", x),
  184. lambda s: s.params.__delitem__("is_lambda"),
  185. None,
  186. )
  187. _globals = property(
  188. lambda s: s.params["_globals"],
  189. lambda s, x: s.params.__setitem__("_globals", x),
  190. lambda s: s.params.__delitem__("_globals"),
  191. None,
  192. )
  193. def set_pos_info(self, node, start, finish, name=None):
  194. if name is None:
  195. name = self.name
  196. if hasattr(node, "offset"):
  197. node.start = start
  198. node.finish = finish
  199. self.offsets[name, node.offset] = node
  200. if hasattr(node, "parent"):
  201. assert node.parent != node
  202. node.start = start
  203. node.finish = finish
  204. self.last_finish = finish
  205. def preorder(self, node=None):
  206. start = len(self.f.getvalue())
  207. super(pysource.SourceWalker, self).preorder(node)
  208. self.set_pos_info(node, start, len(self.f.getvalue()))
  209. return
  210. def table_r_node(self, node):
  211. """General pattern where the last node should should
  212. get the text span attributes of the entire tree"""
  213. start = len(self.f.getvalue())
  214. try:
  215. self.default(node)
  216. except GenericASTTraversalPruningException:
  217. if not hasattr(node[-1], "parent"):
  218. node[-1].parent = node
  219. final = len(self.f.getvalue())
  220. self.set_pos_info(node, start, final)
  221. self.set_pos_info(node[-1], start, final)
  222. raise GenericASTTraversalPruningException
  223. n_slice0 = n_slice1 = n_slice2 = n_slice3 = n_subscript = table_r_node
  224. n_aug_assign_1 = n_print_item = exec_stmt = print_to_item = delete = table_r_node
  225. n_classdefco1 = n_classdefco2 = except_cond1 = except_cond2 = table_r_node
  226. def n_pass(self, node):
  227. start = len(self.f.getvalue()) + len(self.indent)
  228. self.set_pos_info(node, start, start + len("pass"))
  229. self.default(node)
  230. def n_try_except(self, node):
  231. # Note: we could also do this via modifying the
  232. # 5 or so template rules. That is change:
  233. # 'try_except': ( '%|try%:\n%+%c%-%c\n\n', 1, 3 ),
  234. # to:
  235. # 'try_except': ( '%|try%b:\n%+%c%-%c\n\n', 0, 1, 3 ),
  236. start = len(self.f.getvalue()) + len(self.indent)
  237. self.set_pos_info(node[0], start, start + len("try:"))
  238. self.default(node)
  239. n_tryelsestmt = n_c_tryelsestmt = n_tryelsestmtl = n_tryfinallystmt = n_try_except
  240. def n_raise_stmt0(self, node):
  241. assert node[0] == "RAISE_VARARGS_0"
  242. start = len(self.f.getvalue()) + len(self.indent)
  243. try:
  244. self.default(node)
  245. except GenericASTTraversalPruningException:
  246. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  247. self.prune()
  248. def n_raise_stmt1(self, node):
  249. assert node[1] == "RAISE_VARARGS_1"
  250. start = len(self.f.getvalue()) + len(self.indent)
  251. try:
  252. self.default(node)
  253. except GenericASTTraversalPruningException:
  254. self.set_pos_info(node[1], start, len(self.f.getvalue()))
  255. self.prune()
  256. def n_raise_stmt2(self, node):
  257. assert node[2] == "RAISE_VARARGS_2"
  258. start = len(self.f.getvalue()) + len(self.indent)
  259. try:
  260. self.default(node)
  261. except GenericASTTraversalPruningException:
  262. self.set_pos_info(node[2], start, len(self.f.getvalue()))
  263. self.prune()
  264. # FIXME: Isolate: only in Python 2.x.
  265. def n_raise_stmt3(self, node):
  266. assert node[3] == "RAISE_VARARGS_3"
  267. start = len(self.f.getvalue()) + len(self.indent)
  268. try:
  269. self.default(node)
  270. except GenericASTTraversalPruningException:
  271. self.set_pos_info(node[3], start, len(self.f.getvalue()))
  272. self.prune()
  273. def n_return(self, node):
  274. start = len(self.f.getvalue()) + len(self.indent)
  275. if self.params["is_lambda"] or node[0] in (
  276. "pop_return",
  277. "popb_return",
  278. "pop_ex_return",
  279. ):
  280. self.preorder(node[0])
  281. if hasattr(node[-1], "offset"):
  282. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  283. self.prune()
  284. else:
  285. start = len(self.f.getvalue()) + len(self.indent)
  286. self.write(self.indent, "return")
  287. # One reason we worry over whether we use "return None" or "return"
  288. # is that inside a generator, "return None" is illegal.
  289. # Thank you, Python!
  290. if self.return_none or not self.is_return_none(node):
  291. self.write(" ")
  292. self.last_finish = len(self.f.getvalue())
  293. self.preorder(node[0])
  294. if hasattr(node[-1], "offset"):
  295. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  296. pass
  297. pass
  298. else:
  299. for n in node:
  300. self.set_pos_info_recurse(n, start, len(self.f.getvalue()))
  301. pass
  302. pass
  303. self.set_pos_info(node, start, len(self.f.getvalue()))
  304. self.println()
  305. self.prune() # stop recursing
  306. def n_return_if_stmt(self, node):
  307. start = len(self.f.getvalue()) + len(self.indent)
  308. if self.params["is_lambda"]:
  309. node[0].parent = node
  310. self.preorder(node[0])
  311. else:
  312. start = len(self.f.getvalue()) + len(self.indent)
  313. self.write(self.indent, "return")
  314. if self.return_none or not self.is_return_none(node):
  315. self.write(" ")
  316. self.preorder(node[0])
  317. if hasattr(node[-1], "offset"):
  318. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  319. self.println()
  320. self.set_pos_info(node, start, len(self.f.getvalue()))
  321. self.prune() # stop recursing
  322. def n_yield(self, node):
  323. start = len(self.f.getvalue())
  324. try:
  325. super(FragmentsWalker, self).n_yield(node)
  326. except GenericASTTraversalPruningException:
  327. pass
  328. if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
  329. node[0].parent = node
  330. self.set_pos_info(node[-1], start, len(self.f.getvalue()))
  331. self.set_pos_info(node, start, len(self.f.getvalue()))
  332. self.prune() # stop recursing
  333. # In Python 3.3+ only
  334. def n_yield_from(self, node):
  335. start = len(self.f.getvalue())
  336. try:
  337. super(FragmentsWalker, self).n_yield(node)
  338. except GenericASTTraversalPruningException:
  339. pass
  340. self.preorder(node[0])
  341. self.set_pos_info(node, start, len(self.f.getvalue()))
  342. self.prune() # stop recursing
  343. def n_buildslice3(self, node):
  344. start = len(self.f.getvalue())
  345. try:
  346. super(FragmentsWalker, self).n_buildslice3(node)
  347. except GenericASTTraversalPruningException:
  348. pass
  349. self.set_pos_info(node, start, len(self.f.getvalue()))
  350. self.prune() # stop recursing
  351. def n_buildslice2(self, node):
  352. start = len(self.f.getvalue())
  353. try:
  354. super(FragmentsWalker, self).n_buildslice2(node)
  355. except GenericASTTraversalPruningException:
  356. pass
  357. self.set_pos_info(node, start, len(self.f.getvalue()))
  358. self.prune() # stop recursing
  359. def n_expr(self, node):
  360. start = len(self.f.getvalue())
  361. p = self.prec
  362. if node[0].kind.startswith("bin_op"):
  363. n = node[0][-1][0]
  364. else:
  365. n = node[0]
  366. self.prec = PRECEDENCE.get(n.kind, -2)
  367. if n == "LOAD_CONST" and repr(n.pattr)[0] == "-":
  368. n.parent = node
  369. self.set_pos_info(n, start, len(self.f.getvalue()))
  370. self.prec = 6
  371. if p < self.prec:
  372. self.write("(")
  373. node[0].parent = node
  374. self.last_finish = len(self.f.getvalue())
  375. self.preorder(node[0])
  376. finish = len(self.f.getvalue())
  377. if hasattr(node[0], "offset"):
  378. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  379. self.write(")")
  380. self.last_finish = finish + 1
  381. else:
  382. node[0].parent = node
  383. start = len(self.f.getvalue())
  384. self.preorder(node[0])
  385. if hasattr(node[0], "offset"):
  386. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  387. self.prec = p
  388. self.set_pos_info(node, start, len(self.f.getvalue()))
  389. self.prune()
  390. def n_return_expr(self, node):
  391. start = len(self.f.getvalue())
  392. super(FragmentsWalker, self).n_return_expr(node)
  393. self.set_pos_info(node, start, len(self.f.getvalue()))
  394. def n_bin_op(self, node):
  395. """bin_op (formerly "binary_expr") is the Python AST BinOp"""
  396. start = len(self.f.getvalue())
  397. for n in node:
  398. n.parent = node
  399. self.last_finish = len(self.f.getvalue())
  400. try:
  401. super(FragmentsWalker, self).n_bin_op(node)
  402. except GenericASTTraversalPruningException:
  403. pass
  404. self.set_pos_info(node, start, len(self.f.getvalue()))
  405. self.prune()
  406. def n_LOAD_CONST(self, node):
  407. start = len(self.f.getvalue())
  408. try:
  409. super(FragmentsWalker, self).n_LOAD_CONST(node)
  410. except GenericASTTraversalPruningException:
  411. pass
  412. self.set_pos_info(node, start, len(self.f.getvalue()))
  413. self.prune()
  414. n_LOAD_STR = n_LOAD_CONST
  415. def n_ifelsestmtr(self, node):
  416. if node[2] == "COME_FROM":
  417. return_stmts_node = node[3]
  418. node.kind = "ifelsestmtr2"
  419. else:
  420. return_stmts_node = node[2]
  421. if len(return_stmts_node) != 2:
  422. self.default(node)
  423. if not (
  424. return_stmts_node[0][0][0] == "ifstmt"
  425. and return_stmts_node[0][0][0][1][0] == "return_if_stmts"
  426. ) and not (
  427. return_stmts_node[0][-1][0] == "ifstmt"
  428. and return_stmts_node[0][-1][0][1][0] == "return_if_stmts"
  429. ):
  430. self.default(node)
  431. return
  432. start = len(self.f.getvalue()) + len(self.indent)
  433. self.write(self.indent, "if ")
  434. self.preorder(node[0])
  435. self.println(":")
  436. self.indent_more()
  437. node[1].parent = node
  438. self.preorder(node[1])
  439. self.indent_less()
  440. if_ret_at_end = False
  441. if len(node[2][0]) >= 3:
  442. node[2][0].parent = node
  443. if (
  444. node[2][0][-1][0] == "ifstmt"
  445. and node[2][0][-1][0][1][0] == "return_if_stmts"
  446. ):
  447. if_ret_at_end = True
  448. past_else = False
  449. prev_stmt_is_if_ret = True
  450. for n in return_stmts_node[0]:
  451. if n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts":
  452. if prev_stmt_is_if_ret:
  453. n[0].kind = "elifstmt"
  454. prev_stmt_is_if_ret = True
  455. else:
  456. prev_stmt_is_if_ret = False
  457. if not past_else and not if_ret_at_end:
  458. self.println(self.indent, "else:")
  459. self.indent_more()
  460. past_else = True
  461. n.parent = node
  462. self.preorder(n)
  463. if not past_else or if_ret_at_end:
  464. self.println(self.indent, "else:")
  465. self.indent_more()
  466. node[2][1].parent = node
  467. self.preorder(node[2][1])
  468. self.set_pos_info(node, start, len(self.f.getvalue()))
  469. self.indent_less()
  470. self.prune()
  471. def n_elifelsestmtr(self, node):
  472. if len(node[2]) != 2:
  473. self.default(node)
  474. for n in node[2][0]:
  475. if not (n[0] == "ifstmt" and n[0][1][0] == "return_if_stmts"):
  476. self.default(node)
  477. return
  478. start = len(self.f.getvalue() + self.indent)
  479. self.write(self.indent, "elif ")
  480. node[0].parent = node
  481. self.preorder(node[0])
  482. self.println(":")
  483. self.indent_more()
  484. node[1].parent = node
  485. self.preorder(node[1])
  486. self.indent_less()
  487. for n in node[2][0]:
  488. n[0].kind = "elifstmt"
  489. n.parent = node
  490. self.preorder(n)
  491. self.println(self.indent, "else:")
  492. self.indent_more()
  493. node[2][1].parent = node
  494. self.preorder(node[2][1])
  495. self.indent_less()
  496. self.set_pos_info(node, start, len(self.f.getvalue()))
  497. self.prune()
  498. def n_alias(self, node):
  499. start = len(self.f.getvalue())
  500. iname = node[0].pattr
  501. store_import_node = node[-1][-1]
  502. assert store_import_node.kind.startswith("STORE_")
  503. sname = store_import_node.pattr
  504. self.write(iname)
  505. finish = len(self.f.getvalue())
  506. if iname == sname or iname.startswith(sname + "."):
  507. self.set_pos_info_recurse(node, start, finish)
  508. else:
  509. self.write(" as ")
  510. sname_start = len(self.f.getvalue())
  511. self.write(sname)
  512. finish = len(self.f.getvalue())
  513. for n in node[-1]:
  514. self.set_pos_info_recurse(n, sname_start, finish)
  515. self.set_pos_info(node, start, finish)
  516. self.prune() # stop recursing
  517. def n_mkfunc(self, node):
  518. start = len(self.f.getvalue())
  519. if self.version >= (3, 3) or node[-2] == "kwargs":
  520. # LOAD_CONST code object ..
  521. # LOAD_CONST 'x0' if >= 3.3
  522. # MAKE_FUNCTION ..
  523. code_node = node[-3]
  524. elif node[-2] == "expr":
  525. code_node = node[-2][0]
  526. else:
  527. # LOAD_CONST code object ..
  528. # MAKE_FUNCTION ..
  529. code_node = node[-2]
  530. func_name = code_node.attr.co_name
  531. self.write(func_name)
  532. self.set_pos_info(code_node, start, len(self.f.getvalue()))
  533. self.indent_more()
  534. start = len(self.f.getvalue())
  535. make_function36(self, node, is_lambda=False, code_node=code_node)
  536. self.set_pos_info(node, start, len(self.f.getvalue()))
  537. if len(self.param_stack) > 1:
  538. self.write("\n\n")
  539. else:
  540. self.write("\n\n\n")
  541. self.indent_less()
  542. self.prune() # stop recursing
  543. def comprehension_walk(self, node, iter_index):
  544. p = self.prec
  545. self.prec = 27
  546. # FIXME: clean this up
  547. if node == "dict_comp":
  548. cn = node[1]
  549. elif node in ("generator_exp", "generator_exp_async"):
  550. if node[0] == "load_genexpr":
  551. load_genexpr = node[0]
  552. elif node[1] == "load_genexpr":
  553. load_genexpr = node[1]
  554. cn = load_genexpr[0]
  555. else:
  556. if len(node[1]) > 1 and hasattr(node[1][1], "attr"):
  557. # Python 3.3+ does this
  558. cn = node[1][1]
  559. else:
  560. assert False, "Can't find code for comprehension"
  561. assert iscode(cn.attr)
  562. code = Code(cn.attr, self.scanner, self.currentclass)
  563. ast = self.build_ast(code._tokens, code._customize, code)
  564. self.customize(code._customize)
  565. # Remove single reductions as in ("stmts", "sstmt"):
  566. while len(ast) == 1:
  567. ast = ast[0]
  568. n = ast[iter_index]
  569. assert n == "comp_iter", n.kind
  570. # Find the comprehension body. It is the inner-most
  571. # node that is not list_.. .
  572. while n == "comp_iter": # list_iter
  573. n = n[0] # recurse one step
  574. if n == "comp_for":
  575. if n[0] == "SETUP_LOOP":
  576. n = n[4]
  577. else:
  578. n = n[3]
  579. elif n == "comp_if":
  580. n = n[1]
  581. elif n == "comp_if_not":
  582. n = n[2]
  583. assert n == "comp_body", n
  584. self.preorder(n[0])
  585. if node == "generator_exp_async":
  586. self.write(" async")
  587. iter_var_index = iter_index - 2
  588. else:
  589. iter_var_index = iter_index - 1
  590. self.write(" for ")
  591. start = len(self.f.getvalue())
  592. store = ast[iter_var_index]
  593. self.preorder(store)
  594. self.set_pos_info(ast[iter_index - 1], start, len(self.f.getvalue()))
  595. self.write(" in ")
  596. start = len(self.f.getvalue())
  597. if node[2] == "expr":
  598. iter_expr = node[2]
  599. else:
  600. iter_expr = node[-3]
  601. assert iter_expr == "expr"
  602. iter_expr.parent = node
  603. self.preorder(iter_expr)
  604. self.set_pos_info(iter_expr, start, len(self.f.getvalue()))
  605. start = len(self.f.getvalue())
  606. self.preorder(ast[iter_index])
  607. self.set_pos_info(ast[iter_index], start, len(self.f.getvalue()))
  608. self.prec = p
  609. def comprehension_walk3(self, node, iter_index, code_index=-5):
  610. """
  611. List comprehensions the way they are done in Python3.
  612. They're more other comprehensions, e.g. set comprehensions
  613. See if we can combine code.
  614. """
  615. p = self.prec
  616. self.prec = 27
  617. code = node[code_index].attr
  618. assert iscode(code), node[code_index]
  619. code_name = code.co_name
  620. code = Code(code, self.scanner, self.currentclass, self.debug_opts["asm"])
  621. ast = self.build_ast(code._tokens, code._customize, code)
  622. self.customize(code._customize)
  623. if ast[0] == "sstmt":
  624. ast = ast[0]
  625. # skip over stmt return return_expr
  626. ast = ast[0][0][0]
  627. store = None
  628. if ast in ["set_comp_func", "dict_comp_func"]:
  629. # Offset 0: BUILD_SET should have the span
  630. # of '{'
  631. self.gen_source(ast, code_name, {})
  632. for k in ast:
  633. if k == "comp_iter":
  634. n = k
  635. elif k == "store":
  636. store = k
  637. pass
  638. pass
  639. pass
  640. else:
  641. ast = ast[0][0]
  642. n = ast[iter_index]
  643. assert n == "list_iter", n
  644. # FIXME: I'm not totally sure this is right.
  645. # Find the list comprehension body. It is the inner-most
  646. # node that is not list_.. .
  647. if_node = None
  648. comp_for = None
  649. comp_store = None
  650. if n == "comp_iter":
  651. comp_for = n
  652. comp_store = ast[3]
  653. have_not = False
  654. while n in ("list_iter", "comp_iter"):
  655. n = n[0] # iterate one nesting deeper
  656. if n in ("list_for", "comp_for"):
  657. if n[2] == "store":
  658. store = n[2]
  659. n = n[3]
  660. elif n in ("list_if", "list_if_not", "comp_if", "comp_ifnot"):
  661. have_not = n in ("list_if_not", "comp_ifnot")
  662. if_node = n[0]
  663. if n[1] == "store":
  664. store = n[1]
  665. n = n[2]
  666. pass
  667. pass
  668. # Python 2.7+ starts including set_comp_body
  669. # Python 3.5+ starts including set_comp_func
  670. assert n.kind in ("lc_body", "comp_body", "set_comp_func", "set_comp_body"), ast
  671. assert store, "Couldn't find store in list/set comprehension"
  672. old_name = self.name
  673. self.name = code_name
  674. # Issue created with later Python code generation is that there
  675. # is a lambda set up with a dummy argument name that is then called
  676. # So we can't just translate that as is but need to replace the
  677. # dummy name. Below we are picking out the variable name as seen
  678. # in the code. And trying to generate code for the other parts
  679. # that don't have the dummy argument name in it.
  680. # Another approach might be to be able to pass in the source name
  681. # for the dummy argument.
  682. self.preorder(n[0])
  683. gen_start = len(self.f.getvalue()) + 1
  684. self.write(" for ")
  685. start = len(self.f.getvalue())
  686. if comp_store:
  687. self.preorder(comp_store)
  688. else:
  689. self.preorder(store)
  690. self.set_pos_info(store, start, len(self.f.getvalue()))
  691. # FIXME this is all merely approximate
  692. # from trepan.api import debug; debug()
  693. self.write(" in ")
  694. start = len(self.f.getvalue())
  695. node[-3].parent = node
  696. self.preorder(node[-3])
  697. fin = len(self.f.getvalue())
  698. self.set_pos_info(node[-3], start, fin, old_name)
  699. if ast == "list_comp":
  700. list_iter = ast[1]
  701. assert list_iter == "list_iter"
  702. if list_iter == "list_for":
  703. self.preorder(list_iter[3])
  704. self.prec = p
  705. return
  706. pass
  707. if comp_store:
  708. self.preorder(comp_for)
  709. elif if_node:
  710. self.write(" if ")
  711. if have_not:
  712. self.write("not ")
  713. self.preorder(if_node)
  714. pass
  715. self.prec = p
  716. self.name = old_name
  717. if node[-1].kind.startswith("CALL_FUNCTION"):
  718. self.set_pos_info(node[-1], gen_start, len(self.f.getvalue()))
  719. def listcomprehension_walk2(self, node):
  720. """List comprehensions the way they are done in Python 2 (and
  721. some Python 3?).
  722. They're more other comprehensions, e.g. set comprehensions
  723. See if we can combine code.
  724. """
  725. p = self.prec
  726. self.prec = 27
  727. code = Code(node[1].attr, self.scanner, self.currentclass)
  728. ast = self.build_ast(code._tokens, code._customize, code)
  729. self.customize(code._customize)
  730. if node == "set_comp":
  731. ast = ast[0][0][0]
  732. else:
  733. ast = ast[0][0][0][0][0]
  734. if ast == "expr":
  735. ast = ast[0]
  736. n = ast[1]
  737. collection = node[-3]
  738. list_if = None
  739. assert n == "list_iter"
  740. # Find the list comprehension body. It is the inner-most
  741. # node that is not list_.. .
  742. while n == "list_iter":
  743. n = n[0] # recurse one step
  744. if n == "list_for":
  745. store = n[2]
  746. n = n[3]
  747. elif n in ("list_if", "list_if_not"):
  748. # FIXME: just a guess
  749. if n[0].kind == "expr":
  750. list_if = n
  751. else:
  752. list_if = n[1]
  753. n = n[2]
  754. pass
  755. pass
  756. assert n == "lc_body", ast
  757. self.preorder(n[0])
  758. self.write(" for ")
  759. start = len(self.f.getvalue())
  760. self.preorder(store)
  761. self.set_pos_info(store, start, len(self.f.getvalue()))
  762. self.write(" in ")
  763. start = len(self.f.getvalue())
  764. node[-3].parent = node
  765. self.preorder(collection)
  766. self.set_pos_info(collection, start, len(self.f.getvalue()))
  767. if list_if:
  768. start = len(self.f.getvalue())
  769. self.preorder(list_if)
  770. self.set_pos_info(list_if, start, len(self.f.getvalue()))
  771. self.prec = p
  772. def n_generator_exp(self, node):
  773. start = len(self.f.getvalue())
  774. self.write("(")
  775. code_index = -6
  776. self.comprehension_walk(node, iter_index=4, code_index=code_index)
  777. self.write(")")
  778. self.set_pos_info(node, start, len(self.f.getvalue()))
  779. self.prune()
  780. def n_set_comp(self, node):
  781. start = len(self.f.getvalue())
  782. self.write("{")
  783. if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
  784. start = len(self.f.getvalue())
  785. self.set_pos_info(node[0], start - 1, start)
  786. self.comprehension_walk3(node, 1, 0)
  787. elif node[0].kind == "load_closure":
  788. self.closure_walk(node, collection_index=4)
  789. else:
  790. self.comprehension_walk(node, iter_index=4)
  791. self.write("}")
  792. self.set_pos_info(node, start, len(self.f.getvalue()))
  793. self.prune()
  794. # FIXME: Not sure if below is general. Also, add dict_comp_func.
  795. # 'set_comp_func': ("%|lambda %c: {%c for %c in %c%c}\n", 1, 3, 3, 1, 4)
  796. def n_set_comp_func(self, node):
  797. setcomp_start = len(self.f.getvalue())
  798. self.write(self.indent, "lambda ")
  799. param_node = node[1]
  800. start = len(self.f.getvalue())
  801. self.preorder(param_node)
  802. self.set_pos_info(node[0], start, len(self.f.getvalue()))
  803. self.write(": {")
  804. start = len(self.f.getvalue())
  805. assert node[0].kind.startswith("BUILD_SET")
  806. self.set_pos_info(node[0], start - 1, start)
  807. store = node[3]
  808. assert store == "store"
  809. start = len(self.f.getvalue())
  810. self.preorder(store)
  811. fin = len(self.f.getvalue())
  812. self.set_pos_info(store, start, fin)
  813. for_iter_node = node[2]
  814. assert for_iter_node.kind == "FOR_ITER"
  815. self.set_pos_info(for_iter_node, start, fin)
  816. self.write(" for ")
  817. self.preorder(store)
  818. self.write(" in ")
  819. self.preorder(param_node)
  820. start = len(self.f.getvalue())
  821. self.preorder(node[4])
  822. self.set_pos_info(node[4], start, len(self.f.getvalue()))
  823. self.write("}")
  824. fin = len(self.f.getvalue())
  825. self.set_pos_info(node, setcomp_start, fin)
  826. if node[-2] == "RETURN_VALUE":
  827. self.set_pos_info(node[-2], setcomp_start, fin)
  828. self.prune()
  829. def n_list_comp(self, node):
  830. self.write("[")
  831. if node[0].kind == "load_closure":
  832. self.listcomprehension_walk2(node)
  833. else:
  834. if node[0] == "LOAD_LISTCOMP":
  835. start = len(self.f.getvalue())
  836. self.set_pos_info(node[0], start - 1, start)
  837. self.comprehension_walk_newer(node, 1, 0)
  838. self.write("]")
  839. self.prune()
  840. def closure_walk(self, node, collection_index):
  841. """Dictionary and Set comprehensions using closures."""
  842. p = self.prec
  843. self.prec = 27
  844. code = Code(node[1].attr, self.scanner, self.currentclass)
  845. ast = self.build_ast(code._tokens, code._customize, code)
  846. self.customize(code._customize)
  847. ast = ast[0][0][0]
  848. store = ast[3]
  849. collection = node[collection_index]
  850. n = ast[4]
  851. list_if = None
  852. assert n == "comp_iter"
  853. # find inner-most node
  854. while n == "comp_iter":
  855. n = n[0] # recurse one step
  856. # FIXME: adjust for set comprehension
  857. if n == "list_for":
  858. store = n[2]
  859. n = n[3]
  860. elif n in ("list_if", "list_if_not", "comp_if", "comp_if_not"):
  861. # FIXME: just a guess
  862. if n[0].kind == "expr":
  863. list_if = n
  864. else:
  865. list_if = n[1]
  866. n = n[2]
  867. pass
  868. pass
  869. assert n == "comp_body", ast
  870. self.preorder(n[0])
  871. self.write(" for ")
  872. start = len(self.f.getvalue())
  873. self.preorder(store)
  874. self.set_pos_info(store, start, len(self.f.getvalue()))
  875. self.write(" in ")
  876. start = len(self.f.getvalue())
  877. self.preorder(collection)
  878. self.set_pos_info(collection, start, len(self.f.getvalue()))
  879. if list_if:
  880. start = len(self.f.getvalue())
  881. self.preorder(list_if)
  882. self.set_pos_info(list_if, start, len(self.f.getvalue()))
  883. self.prec = p
  884. def n_classdef(self, node):
  885. # class definition ('class X(A,B,C):')
  886. cclass = self.currentclass
  887. if self.version >= (3, 1):
  888. if node == "classdefdeco2":
  889. currentclass = node[1][2].pattr
  890. buildclass = node
  891. else:
  892. currentclass = node[1][0].pattr
  893. buildclass = node[0]
  894. if buildclass[0] == "LOAD_BUILD_CLASS":
  895. start = len(self.f.getvalue())
  896. self.set_pos_info(buildclass[0], start, start + len("class") + 2)
  897. assert "mkfunc" == buildclass[1]
  898. mkfunc = buildclass[1]
  899. if mkfunc[0] == "kwargs":
  900. for n in mkfunc:
  901. if hasattr(n, "attr") and iscode(n.attr):
  902. subclass = n.attr
  903. break
  904. pass
  905. subclass_info = node if node == "classdefdeco2" else node[0]
  906. elif buildclass[1][0] == "load_closure":
  907. # Python 3 with closures not functions
  908. load_closure = buildclass[1]
  909. if hasattr(load_closure[-3], "attr"):
  910. # Python 3.3 classes with closures work like this.
  911. # Note have to test before 3.2 case because
  912. # index -2 also has an attr.
  913. subclass = load_closure[-3].attr
  914. elif hasattr(load_closure[-2], "attr"):
  915. # Python 3.2 works like this
  916. subclass = load_closure[-2].attr
  917. else:
  918. raise RuntimeError(
  919. "Internal Error n_classdef: cannot find class body"
  920. )
  921. if hasattr(buildclass[3], "__len__"):
  922. subclass_info = buildclass[3]
  923. elif hasattr(buildclass[2], "__len__"):
  924. subclass_info = buildclass[2]
  925. else:
  926. raise RuntimeError(
  927. "Internal Error n_classdef: cannot superclass name"
  928. )
  929. else:
  930. subclass = buildclass[1][0].attr
  931. subclass_info = node[0]
  932. else:
  933. buildclass = node if (node == "classdefdeco2") else node[0]
  934. build_list = buildclass[1][0]
  935. if hasattr(buildclass[-3][0], "attr"):
  936. subclass = buildclass[-3][0].attr
  937. currentclass = buildclass[0].pattr
  938. elif hasattr(node[0][0], "pattr"):
  939. subclass = buildclass[-3][1].attr
  940. currentclass = node[0][0].pattr
  941. else:
  942. raise RuntimeError("Internal Error n_classdef: cannot find class name")
  943. if node == "classdefdeco2":
  944. self.write("\n")
  945. else:
  946. self.write("\n\n")
  947. self.currentclass = str(currentclass)
  948. start = len(self.f.getvalue())
  949. self.write(self.indent, "class ", self.currentclass)
  950. if self.version >= (3, 1):
  951. self.print_super_classes3(subclass_info)
  952. else:
  953. self.print_super_classes(build_list)
  954. self.println(":")
  955. # class body
  956. self.indent_more()
  957. self.build_class(subclass)
  958. self.indent_less()
  959. self.currentclass = cclass
  960. self.set_pos_info(node, start, len(self.f.getvalue()))
  961. if len(self.param_stack) > 1:
  962. self.write("\n\n")
  963. else:
  964. self.write("\n\n\n")
  965. self.prune()
  966. n_classdefdeco2 = n_classdef
  967. def gen_source(
  968. self,
  969. ast,
  970. name,
  971. customize,
  972. is_lambda=False,
  973. returnNone=False,
  974. debug_opts=DEFAULT_DEBUG_OPTS,
  975. ):
  976. """convert parse tree to Python source code"""
  977. rn = self.return_none
  978. self.return_none = returnNone
  979. old_name = self.name
  980. self.name = name
  981. self.debug_opts = debug_opts
  982. # if code would be empty, append 'pass'
  983. if len(ast) == 0:
  984. self.println(self.indent, "pass")
  985. else:
  986. self.customize(customize)
  987. self.text = self.traverse(ast, is_lambda=is_lambda)
  988. self.name = old_name
  989. self.return_none = rn
  990. def build_ast(
  991. self,
  992. tokens,
  993. customize,
  994. code,
  995. is_lambda=False,
  996. noneInNames=False,
  997. is_top_level_module=False,
  998. ) -> GenericASTTraversal:
  999. # FIXME: DRY with pysource.py
  1000. # assert isinstance(tokens[0], Token)
  1001. if is_lambda:
  1002. for t in tokens:
  1003. if t.kind == "RETURN_END_IF":
  1004. t.kind = "RETURN_END_IF_LAMBDA"
  1005. elif t.kind == "RETURN_VALUE":
  1006. t.kind = "RETURN_VALUE_LAMBDA"
  1007. tokens.append(Token("LAMBDA_MARKER", optyhpe="pseudo"))
  1008. try:
  1009. if self.p_lambda is None:
  1010. self.p_lambda = get_python_parser(
  1011. self.version,
  1012. self.debug_parser,
  1013. compile_mode="lambda",
  1014. is_pypy=self.is_pypy,
  1015. )
  1016. p = self.p_lambda
  1017. p.insts = self.scanner.insts
  1018. p.offset2inst_index = self.scanner.offset2inst_index
  1019. parse_tree = python_parser.parse(p, tokens, customize, is_lambda)
  1020. self.customize(customize)
  1021. except (heads.ParserError, AssertionError) as e:
  1022. raise ParserError(e, tokens, self.debug_parser.get("reduce", False))
  1023. # FIXME: So as not to remove tokens with offsets,
  1024. # remove this phase until we have a chance to go over,
  1025. # transform_tree = self.treeTransform.transform(ast)
  1026. return parse_tree
  1027. # del ast # Save memory
  1028. # return transform_tree
  1029. # The bytecode for the end of the main routine has a
  1030. # "return None". However you can't issue a "return" statement in
  1031. # main. In the other build_ast routine we eliminate the
  1032. # return statement instructions before parsing.
  1033. # But here we want to keep these instructions at the expense of
  1034. # a fully runnable Python program because we
  1035. # my be queried about the role of one of those instructions.
  1036. #
  1037. # NOTE: this differs from behavior in pysource.py
  1038. if len(tokens) >= 2 and not noneInNames:
  1039. if tokens[-1].kind in ("RETURN_VALUE", "RETURN_VALUE_LAMBDA"):
  1040. # Python 3.4's classes can add a "return None" which is
  1041. # invalid syntax.
  1042. if tokens[-2].kind == "LOAD_CONST":
  1043. if is_top_level_module or tokens[-2].pattr is None:
  1044. del tokens[-2:]
  1045. else:
  1046. tokens.append(Token("RETURN_LAST"))
  1047. else:
  1048. tokens.append(Token("RETURN_LAST"))
  1049. if len(tokens) == 0:
  1050. return PASS
  1051. # Build a parse tree from tokenized and massaged disassembly.
  1052. try:
  1053. # FIXME: have p.insts update in a better way
  1054. # modularity is broken here
  1055. p_insts = self.p.insts
  1056. self.p.insts = self.scanner.insts
  1057. self.p.offset2inst_index = self.scanner.offset2inst_index
  1058. self.p.opc = self.scanner.opc
  1059. parse_tree = python_parser.parse(
  1060. self.p, tokens, customize, is_lambda=is_lambda
  1061. )
  1062. self.p.insts = p_insts
  1063. except (heads.ParserError, AssertionError) as e:
  1064. raise ParserError(e, tokens, self.debug_parser.get("reduce", False))
  1065. checker(parse_tree, False, self.ast_errors)
  1066. return parse_tree
  1067. # FIXME: we could provide another customized routine
  1068. # that fixes up parents along a particular path to a node that
  1069. # we are interested in.
  1070. def fixup_parents(self, node, parent):
  1071. """Make sure each node has a parent"""
  1072. start, finish = 0, self.last_finish
  1073. # We assume anything with a start has a finish.
  1074. needs_range = not hasattr(node, "start")
  1075. if not hasattr(node, "parent"):
  1076. node.parent = parent
  1077. for n in node:
  1078. if needs_range and hasattr(n, "start"):
  1079. if n.start < start:
  1080. start = n.start
  1081. if n.finish > finish:
  1082. finish = n.finish
  1083. if hasattr(n, "offset") and not hasattr(n, "parent"):
  1084. n.parent = node
  1085. else:
  1086. self.fixup_parents(n, node)
  1087. pass
  1088. pass
  1089. if needs_range:
  1090. node.start, node.finish = start, finish
  1091. return
  1092. # FIXME: revise to do *once* over the entire tree.
  1093. # So here we should just mark that the subtree
  1094. # needs offset adjustment.
  1095. def fixup_offsets(self, new_start, node):
  1096. """Adjust all offsets under node"""
  1097. if hasattr(node, "start"):
  1098. node.start += new_start
  1099. node.finish += new_start
  1100. for n in node:
  1101. if hasattr(n, "offset"):
  1102. if hasattr(n, "start"):
  1103. n.start += new_start
  1104. n.finish += new_start
  1105. else:
  1106. self.fixup_offsets(new_start, n)
  1107. return
  1108. def set_pos_info_recurse(self, node, start, finish, parent=None):
  1109. """Set positions under node"""
  1110. self.set_pos_info(node, start, finish)
  1111. if parent is None:
  1112. parent = node
  1113. for n in node:
  1114. n.parent = parent
  1115. if hasattr(n, "offset"):
  1116. self.set_pos_info(n, start, finish)
  1117. else:
  1118. n.start = start
  1119. n.finish = finish
  1120. self.set_pos_info_recurse(n, start, finish, parent)
  1121. return
  1122. def node_append(self, before_str, node_text, node):
  1123. self.write(before_str)
  1124. self.last_finish = len(self.f.getvalue())
  1125. self.fixup_offsets(self.last_finish, node)
  1126. self.write(node_text)
  1127. self.last_finish = len(self.f.getvalue())
  1128. # FIXME: duplicated from pysource, since we don't find self.params
  1129. def traverse(self, node, indent=None, is_lambda=False):
  1130. """Builds up fragment which can be used inside a larger
  1131. block of code"""
  1132. self.param_stack.append(self.params)
  1133. if indent is None:
  1134. indent = self.indent
  1135. p = self.pending_newlines
  1136. self.pending_newlines = 0
  1137. self.params = {
  1138. "_globals": {},
  1139. "_nonlocals": {}, # Python 3 has nonlocal
  1140. "f": StringIO(),
  1141. "indent": indent,
  1142. "is_lambda": is_lambda,
  1143. }
  1144. self.preorder(node)
  1145. self.f.write("\n" * self.pending_newlines)
  1146. text = self.f.getvalue()
  1147. self.last_finish = len(text)
  1148. self.params = self.param_stack.pop()
  1149. self.pending_newlines = p
  1150. return text
  1151. def extract_node_info(self, nodeInfo):
  1152. # XXX debug
  1153. # print('-' * 30)
  1154. # node = nodeInfo.node
  1155. # print(node)
  1156. # if hasattr(node, 'parent'):
  1157. # print('~' * 30)
  1158. # print(node.parent)
  1159. # else:
  1160. # print("No parent")
  1161. # print('-' * 30)
  1162. start, finish = (nodeInfo.start, nodeInfo.finish)
  1163. text = self.text
  1164. # Ignore trailing blanks
  1165. match = re.search(r"\n+$", text[start:])
  1166. if match:
  1167. text = text[: -len(match.group(0))]
  1168. # Ignore leading blanks
  1169. match = re.search(r"\s*[^ \t\n]", text[start:])
  1170. if match:
  1171. start += len(match.group(0)) - 1
  1172. at_end = False
  1173. if start >= finish:
  1174. at_end = True
  1175. selectedText = text
  1176. else:
  1177. selectedText = text[start:finish]
  1178. # Compute offsets relative to the beginning of the
  1179. # line rather than the beginning of the text.
  1180. try:
  1181. lineStart = text[:start].rindex("\n") + 1
  1182. except ValueError:
  1183. lineStart = 0
  1184. adjustedStart = start - lineStart
  1185. # If selected text is greater than a single line
  1186. # just show the first line plus ellipsis.
  1187. lines = selectedText.split("\n")
  1188. if len(lines) > 1:
  1189. adjustedEnd = len(lines[0]) - adjustedStart
  1190. selectedText = lines[0] + " ...\n" + lines[-1]
  1191. else:
  1192. adjustedEnd = len(selectedText)
  1193. if at_end:
  1194. markerLine = (" " * len(lines[-1])) + "^"
  1195. else:
  1196. markerLine = (" " * adjustedStart) + ("-" * adjustedEnd)
  1197. elided = False
  1198. if len(lines) > 1 and not at_end:
  1199. elided = True
  1200. markerLine += " ..."
  1201. # Get line that the selected text is in and
  1202. # get a line count for that.
  1203. try:
  1204. lineEnd = lineStart + text[lineStart + 1 :].index("\n") - 1
  1205. except ValueError:
  1206. lineEnd = len(text)
  1207. lines = text[:lineEnd].split("\n")
  1208. selectedLine = text[lineStart : lineEnd + 2]
  1209. if elided:
  1210. selectedLine += " ..."
  1211. if isinstance(nodeInfo, Token):
  1212. nodeInfo = nodeInfo.parent
  1213. else:
  1214. nodeInfo = nodeInfo
  1215. if isinstance(nodeInfo, SyntaxTree):
  1216. nonterminal = nodeInfo[0]
  1217. else:
  1218. nonterminal = nodeInfo.node
  1219. return ExtractInfo(
  1220. lineNo=len(lines),
  1221. lineStartOffset=lineStart,
  1222. markerLine=markerLine,
  1223. selectedLine=selectedLine,
  1224. selectedText=selectedText,
  1225. nonterminal=nonterminal,
  1226. )
  1227. def extract_line_info(self, name, offset):
  1228. if (name, offset) not in list(self.offsets.keys()):
  1229. return None
  1230. return self.extract_node_info(self.offsets[name, offset])
  1231. def prev_node(self, node):
  1232. prev = None
  1233. if not hasattr(node, "parent"):
  1234. return prev
  1235. p = node.parent
  1236. for n in p:
  1237. if node == n:
  1238. return prev
  1239. prev = n
  1240. return prev
  1241. def extract_parent_info(self, node):
  1242. if not hasattr(node, "parent"):
  1243. return None, None
  1244. p = node.parent
  1245. orig_parent = p
  1246. # If we can get different text, use that as the parent,
  1247. # otherwise we'll use the immediatate parent.
  1248. while p and (
  1249. hasattr(p, "parent") and p.start == node.start and p.finish == node.finish
  1250. ):
  1251. assert p != node
  1252. node = p
  1253. p = p.parent
  1254. if p is None:
  1255. p = orig_parent
  1256. return self.extract_node_info(p), p
  1257. def print_super_classes(self, node):
  1258. if not (node == "build_list"):
  1259. return
  1260. start = len(self.f.getvalue())
  1261. self.write("(")
  1262. line_separator = ", "
  1263. sep = ""
  1264. for elem in node[:-1]:
  1265. value = self.traverse(elem)
  1266. self.node_append(sep, value, elem)
  1267. # self.write(sep, value)
  1268. sep = line_separator
  1269. self.write(")")
  1270. self.set_pos_info(node, start, len(self.f.getvalue()))
  1271. def print_super_classes3(self, node):
  1272. # FIXME: wrap superclasses onto a node
  1273. # as a custom rule
  1274. start = len(self.f.getvalue())
  1275. n = len(node) - 1
  1276. assert node[n].kind.startswith("CALL_FUNCTION")
  1277. for i in range(n - 2, 0, -1):
  1278. if not node[i].kind in ["expr", "LOAD_CLASSNAME"]:
  1279. break
  1280. pass
  1281. if i == n - 2:
  1282. return
  1283. self.write("(")
  1284. line_separator = ", "
  1285. sep = ""
  1286. i += 1
  1287. while i < n:
  1288. value = self.traverse(node[i])
  1289. self.node_append(sep, value, node[i])
  1290. i += 1
  1291. self.write(sep, value)
  1292. sep = line_separator
  1293. self.write(")")
  1294. self.set_pos_info(node, start, len(self.f.getvalue()))
  1295. def n_dict(self, node):
  1296. """
  1297. prettyprint a dict
  1298. 'dict' is something like k = {'a': 1, 'b': 42 }"
  1299. """
  1300. p = self.prec
  1301. self.prec = 100
  1302. self.indent_more(INDENT_PER_LEVEL)
  1303. line_seperator = ",\n" + self.indent
  1304. sep = INDENT_PER_LEVEL[:-1]
  1305. start = len(self.f.getvalue())
  1306. self.write("{")
  1307. self.set_pos_info(node[0], start, start + 1)
  1308. if self.version >= (3, 0) and not self.is_pypy:
  1309. if node[0].kind.startswith("kvlist"):
  1310. # Python 3.5+ style key/value list in dict
  1311. kv_node = node[0]
  1312. ll = list(kv_node)
  1313. length = len(ll)
  1314. if kv_node[-1].kind.startswith("BUILD_MAP"):
  1315. length -= 1
  1316. i = 0
  1317. while i < length:
  1318. self.write(sep)
  1319. name = self.traverse(ll[i], indent="")
  1320. ll[i].parent = kv_node
  1321. ll[i + 1].parent = kv_node
  1322. self.write(name, ": ")
  1323. value = self.traverse(
  1324. ll[i + 1], indent=self.indent + (len(name) + 2) * " "
  1325. )
  1326. self.write(sep, name, ": ", value)
  1327. sep = line_seperator
  1328. i += 2
  1329. pass
  1330. pass
  1331. elif node[1].kind.startswith("kvlist"):
  1332. # Python 3.0..3.4 style key/value list in dict
  1333. kv_node = node[1]
  1334. ll = list(kv_node)
  1335. if len(ll) > 0 and ll[0].kind == "kv3":
  1336. # Python 3.2 does this
  1337. kv_node = node[1][0]
  1338. ll = list(kv_node)
  1339. i = 0
  1340. while i < len(ll):
  1341. ll[i].parent = kv_node
  1342. ll[i + 1].parent = kv_node
  1343. key_start = len(self.f.getvalue()) + len(sep)
  1344. name = self.traverse(ll[i + 1], indent="")
  1345. key_finish = key_start + len(name)
  1346. val_start = key_finish + 2
  1347. value = self.traverse(
  1348. ll[i], indent=self.indent + (len(name) + 2) * " "
  1349. )
  1350. self.write(sep, name, ": ", value)
  1351. self.set_pos_info_recurse(ll[i + 1], key_start, key_finish)
  1352. self.set_pos_info_recurse(ll[i], val_start, val_start + len(value))
  1353. sep = line_seperator
  1354. i += 3
  1355. pass
  1356. pass
  1357. pass
  1358. else:
  1359. # Python 2 style kvlist
  1360. assert node[-1].kind.startswith("kvlist")
  1361. kv_node = node[-1] # goto kvlist
  1362. for kv in kv_node:
  1363. assert kv in ("kv", "kv2", "kv3")
  1364. # kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR
  1365. # kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR
  1366. # kv3 ::= expr expr STORE_MAP
  1367. if kv == "kv":
  1368. name = self.traverse(kv[-2], indent="")
  1369. kv[1].parent = kv_node
  1370. value = self.traverse(
  1371. kv[1], indent=self.indent + (len(name) + 2) * " "
  1372. )
  1373. elif kv == "kv2":
  1374. name = self.traverse(kv[1], indent="")
  1375. kv[-3].parent = kv_node
  1376. value = self.traverse(
  1377. kv[-3], indent=self.indent + (len(name) + 2) * " "
  1378. )
  1379. elif kv == "kv3":
  1380. name = self.traverse(kv[-2], indent="")
  1381. kv[0].parent = kv_node
  1382. value = self.traverse(
  1383. kv[0], indent=self.indent + (len(name) + 2) * " "
  1384. )
  1385. self.write(sep, name, ": ", value)
  1386. sep = line_seperator
  1387. self.write("}")
  1388. finish = len(self.f.getvalue())
  1389. self.set_pos_info(node, start, finish)
  1390. self.indent_less(INDENT_PER_LEVEL)
  1391. self.prec = p
  1392. self.prune()
  1393. def n_list(self, node):
  1394. """
  1395. prettyprint a list or tuple
  1396. """
  1397. p = self.prec
  1398. self.prec = PRECEDENCE["yield"] - 1
  1399. n = node.pop()
  1400. lastnode = n.kind
  1401. start = len(self.f.getvalue())
  1402. if lastnode.startswith("BUILD_LIST"):
  1403. self.write("[")
  1404. endchar = "]"
  1405. elif lastnode.startswith("BUILD_TUPLE"):
  1406. self.write("(")
  1407. endchar = ")"
  1408. elif lastnode.startswith("BUILD_SET"):
  1409. self.write("{")
  1410. endchar = "}"
  1411. elif lastnode.startswith("ROT_TWO"):
  1412. self.write("(")
  1413. endchar = ")"
  1414. else:
  1415. raise RuntimeError("Internal Error: n_list expects list or tuple")
  1416. flat_elems = []
  1417. for elem in node:
  1418. if elem == "expr1024":
  1419. for subelem in elem:
  1420. for subsubelem in subelem:
  1421. flat_elems.append(subsubelem)
  1422. elif elem == "expr32":
  1423. for subelem in elem:
  1424. flat_elems.append(subelem)
  1425. else:
  1426. flat_elems.append(elem)
  1427. self.indent_more(INDENT_PER_LEVEL)
  1428. if len(node) > 3:
  1429. line_separator = ",\n" + self.indent
  1430. else:
  1431. line_separator = ", "
  1432. sep = INDENT_PER_LEVEL[:-1]
  1433. # FIXME:
  1434. # if flat_elems > some_number, then group
  1435. # do automatic wrapping
  1436. for elem in flat_elems:
  1437. if elem == "ROT_THREE":
  1438. continue
  1439. assert elem == "expr"
  1440. value = self.traverse(elem)
  1441. self.node_append(sep, value, elem)
  1442. sep = line_separator
  1443. if len(node) == 1 and lastnode.startswith("BUILD_TUPLE"):
  1444. self.write(",")
  1445. self.write(endchar)
  1446. finish = len(self.f.getvalue())
  1447. n.parent = node.parent
  1448. self.set_pos_info(n, start, finish)
  1449. self.set_pos_info(node, start, finish)
  1450. self.indent_less(INDENT_PER_LEVEL)
  1451. self.prec = p
  1452. self.prune()
  1453. return
  1454. n_set = n_tuple = n_build_set = n_list
  1455. def template_engine(self, entry, startnode):
  1456. """The format template interpretation engine. See the comment at the
  1457. beginning of this module for how we interpret format
  1458. specifications such as %c, %C, and so on.
  1459. """
  1460. # print("-----")
  1461. # print(startnode.kind)
  1462. # print(entry[0])
  1463. # print('======')
  1464. startnode_start = len(self.f.getvalue())
  1465. start = startnode_start
  1466. fmt = entry[0]
  1467. arg = 1
  1468. i = 0
  1469. lastC = -1
  1470. recurse_node = False
  1471. m = escape.search(fmt)
  1472. while m:
  1473. i = m.end()
  1474. self.write(m.group("prefix"))
  1475. typ = m.group("type") or "{"
  1476. node = startnode
  1477. try:
  1478. if m.group("child"):
  1479. node = node[int(m.group("child"))]
  1480. node.parent = startnode
  1481. except Exception:
  1482. print(node.__dict__)
  1483. raise
  1484. if typ == "%":
  1485. start = len(self.f.getvalue())
  1486. self.write("%")
  1487. self.set_pos_info(node, start, len(self.f.getvalue()))
  1488. elif typ == "+":
  1489. self.indent_more()
  1490. elif typ == "-":
  1491. self.indent_less()
  1492. elif typ == "|":
  1493. self.write(self.indent)
  1494. # no longer used, since BUILD_TUPLE_n is pretty printed:
  1495. elif typ == "r":
  1496. recurse_node = True
  1497. elif typ == ",":
  1498. if lastC == 1:
  1499. self.write(",")
  1500. elif typ == "b":
  1501. finish = len(self.f.getvalue())
  1502. self.set_pos_info(node[entry[arg]], start, finish)
  1503. arg += 1
  1504. elif typ == "c":
  1505. start = len(self.f.getvalue())
  1506. index = entry[arg]
  1507. if isinstance(index, tuple):
  1508. if isinstance(index[1], str):
  1509. assert (
  1510. node[index[0]] == index[1]
  1511. ), "at %s[%d], expected %s node; got %s" % (
  1512. node.kind,
  1513. arg,
  1514. node[index[0]].kind,
  1515. index[1],
  1516. )
  1517. else:
  1518. assert (
  1519. node[index[0]] in index[1]
  1520. ), "at %s[%d], expected to be in '%s' node; got '%s'" % (
  1521. node.kind,
  1522. arg,
  1523. index[1],
  1524. node[index[0]].kind,
  1525. )
  1526. index = index[0]
  1527. assert isinstance(
  1528. index, int
  1529. ), "at %s[%d], %s should be int or tuple" % (
  1530. node.kind,
  1531. arg,
  1532. type(index),
  1533. )
  1534. try:
  1535. node[index]
  1536. except IndexError:
  1537. raise RuntimeError(
  1538. f"""
  1539. Expanding '{node.kind}' in template '{entry}[{arg}]':
  1540. {index} is invalid; has only {len(node)} entries
  1541. """
  1542. )
  1543. self.preorder(node[index])
  1544. finish = len(self.f.getvalue())
  1545. self.set_pos_info(node, start, finish)
  1546. arg += 1
  1547. elif typ == "p":
  1548. p = self.prec
  1549. # entry[arg]
  1550. tup = entry[arg]
  1551. assert isinstance(tup, tuple)
  1552. if len(tup) == 3:
  1553. (index, nonterm_name, self.prec) = tup
  1554. if isinstance(tup[1], str):
  1555. # if node[index] != nonterm_name:
  1556. # from trepan.api import debug; debug()
  1557. assert (
  1558. node[index] == nonterm_name
  1559. ), "at %s[%d], expected '%s' node; got '%s'" % (
  1560. node.kind,
  1561. arg,
  1562. nonterm_name,
  1563. node[index].kind,
  1564. )
  1565. else:
  1566. assert node[tup[0]] in tup[1], (
  1567. f"at {node.kind}[{tup[0]}], expected to be in '{tup[1]}' "
  1568. f"node; got '{node[tup[0]].kind}'"
  1569. )
  1570. else:
  1571. assert len(tup) == 2
  1572. (index, self.prec) = entry[arg]
  1573. node[index].parent = node
  1574. start = len(self.f.getvalue())
  1575. self.preorder(node[index])
  1576. self.set_pos_info(node, start, len(self.f.getvalue()))
  1577. self.prec = p
  1578. arg += 1
  1579. elif typ == "C":
  1580. low, high, sep = entry[arg]
  1581. lastC = remaining = len(node[low:high])
  1582. start = len(self.f.getvalue())
  1583. for subnode in node[low:high]:
  1584. self.preorder(subnode)
  1585. remaining -= 1
  1586. if remaining > 0:
  1587. self.write(sep)
  1588. self.set_pos_info(node, start, len(self.f.getvalue()))
  1589. arg += 1
  1590. elif typ == "D":
  1591. low, high, sep = entry[arg]
  1592. lastC = remaining = len(node[low:high])
  1593. for subnode in node[low:high]:
  1594. remaining -= 1
  1595. if len(subnode) > 0:
  1596. self.preorder(subnode)
  1597. if remaining > 0:
  1598. self.write(sep)
  1599. pass
  1600. pass
  1601. pass
  1602. arg += 1
  1603. elif typ == "x":
  1604. src, dest = entry[arg]
  1605. for d in dest:
  1606. self.set_pos_info_recurse(
  1607. node[d], node[src].start, node[src].finish
  1608. )
  1609. pass
  1610. arg += 1
  1611. elif typ == "P":
  1612. p = self.prec
  1613. low, high, sep, self.prec = entry[arg]
  1614. lastC = remaining = len(node[low:high])
  1615. start = self.last_finish
  1616. for subnode in node[low:high]:
  1617. self.preorder(subnode)
  1618. remaining -= 1
  1619. if remaining > 0:
  1620. self.write(sep)
  1621. self.prec = p
  1622. arg += 1
  1623. elif typ == "{":
  1624. d = node.__dict__
  1625. expr = m.group("expr")
  1626. # Line mapping stuff
  1627. if (
  1628. hasattr(node, "linestart")
  1629. and node.linestart
  1630. and hasattr(node, "current_line_number")
  1631. ):
  1632. self.source_linemap[self.current_line_number] = node.linestart
  1633. # Additional fragment-position stuff
  1634. try:
  1635. start = len(self.f.getvalue())
  1636. self.write(eval(expr, d, d))
  1637. self.set_pos_info(node, start, len(self.f.getvalue()))
  1638. except Exception:
  1639. print(node)
  1640. raise
  1641. m = escape.search(fmt, i)
  1642. pass
  1643. self.write(fmt[i:])
  1644. fin = len(self.f.getvalue())
  1645. if recurse_node:
  1646. self.set_pos_info_recurse(startnode, startnode_start, fin)
  1647. else:
  1648. self.set_pos_info(startnode, startnode_start, fin)
  1649. # FIXME figure out how to get these cases to be table driven.
  1650. # 2. subroutine calls. It the last op is the call and for purposes of printing
  1651. # we don't need to print anything special there. However it encompasses the
  1652. # entire string of the node fn(...)
  1653. if startnode.kind == "call":
  1654. last_node = startnode[-1]
  1655. self.set_pos_info(last_node, startnode_start, self.last_finish)
  1656. return
  1657. def _get_mapping(self, node):
  1658. if (
  1659. hasattr(node, "data")
  1660. and len(node) > 0
  1661. and isinstance(node[-1], Token)
  1662. and not hasattr(node[-1], "parent")
  1663. ):
  1664. node[-1].parent = node
  1665. return MAP.get(node, self.MAP_DIRECT_FRAGMENT)
  1666. pass
  1667. #
  1668. DEFAULT_DEBUG_OPTS = {"asm": False, "tree": False, "grammar": False}
  1669. # This interface is deprecated
  1670. def deparse_code(
  1671. version,
  1672. co,
  1673. out=StringIO(),
  1674. showasm=False,
  1675. showast=False,
  1676. showgrammar=False,
  1677. code_objects={},
  1678. compile_mode="exec",
  1679. is_pypy=IS_PYPY,
  1680. walker=FragmentsWalker,
  1681. ):
  1682. debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
  1683. return code_deparse(
  1684. co,
  1685. out,
  1686. version=version,
  1687. debug_opts=debug_opts,
  1688. code_objects=code_objects,
  1689. compile_mode=compile_mode,
  1690. is_pypy=is_pypy,
  1691. walker=walker,
  1692. )
  1693. def code_deparse(
  1694. co,
  1695. out=StringIO(),
  1696. version=None,
  1697. is_pypy=IS_PYPY,
  1698. debug_opts=DEFAULT_DEBUG_OPTS,
  1699. code_objects={},
  1700. compile_mode="exec",
  1701. walker=FragmentsWalker,
  1702. start_offset: int = 0,
  1703. stop_offset: int = -1,
  1704. ):
  1705. """
  1706. Convert the code object co into a python source fragment.
  1707. :param version: The python version this code is from as a float, for
  1708. example 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 etc.
  1709. :param co: The code object to parse.
  1710. :param out: File like object to write the output to.
  1711. :param debug_opts: A dictionary with keys
  1712. 'asm': value determines whether to show
  1713. mangled bytecode disassembly
  1714. 'ast': value determines whether to show
  1715. 'grammar': boolean determining whether to show
  1716. grammar reduction rules.
  1717. If value is a file-like object, output that object's write method will
  1718. be used rather than sys.stdout
  1719. :return: The deparsed source fragment.
  1720. """
  1721. assert iscode(co)
  1722. if version is None:
  1723. version = PYTHON_VERSION_TRIPLE
  1724. if is_pypy is None:
  1725. is_pypy = IS_PYPY
  1726. # store final output stream for case of error
  1727. scanner = get_scanner(version, is_pypy=is_pypy, show_asm=debug_opts["asm"])
  1728. show_asm = debug_opts.get("asm", None)
  1729. tokens, customize = scanner.ingest(co, code_objects=code_objects, show_asm=show_asm)
  1730. if start_offset > 0:
  1731. for i, t in enumerate(tokens):
  1732. # If t.offset is a string, we want to skip this.
  1733. if isinstance(t.offset, int) and t.offset >= start_offset:
  1734. tokens = tokens[i:]
  1735. break
  1736. if stop_offset > -1:
  1737. for i, t in enumerate(tokens):
  1738. # In contrast to the test for start_offset If t.offset is
  1739. # a string, we want to extract the integer offset value.
  1740. if t.off2int() >= stop_offset:
  1741. tokens = tokens[:i]
  1742. break
  1743. debug_parser = dict(PARSER_DEFAULT_DEBUG)
  1744. # Build Syntax Tree from disassembly.
  1745. linestarts = dict(scanner.opc.findlinestarts(co))
  1746. deparsed = walker(
  1747. version,
  1748. scanner,
  1749. showast=debug_opts.get("tree", TREE_DEFAULT_DEBUG),
  1750. debug_parser=debug_parser,
  1751. compile_mode=compile_mode,
  1752. is_pypy=is_pypy,
  1753. linestarts=linestarts,
  1754. )
  1755. is_top_level_module = co.co_name == "<module>"
  1756. deparsed.ast = deparsed.build_ast(
  1757. tokens, customize, co, is_top_level_module=is_top_level_module
  1758. )
  1759. assert deparsed.ast == "stmts", "Should have parsed grammar start"
  1760. # save memory
  1761. del tokens
  1762. # convert leading '__doc__ = "..." into doc string
  1763. assert deparsed.ast == "stmts"
  1764. (deparsed.mod_globs, _) = pysource.find_globals_and_nonlocals(
  1765. deparsed.ast, set(), set(), co, version
  1766. )
  1767. # Just when you think we've forgotten about what we
  1768. # were supposed to do: Generate source from the Syntax tree!
  1769. deparsed.gen_source(deparsed.ast, co.co_name, customize)
  1770. deparsed.set_pos_info(deparsed.ast, 0, len(deparsed.text))
  1771. deparsed.fixup_parents(deparsed.ast, None)
  1772. for g in sorted(deparsed.mod_globs):
  1773. deparsed.write("# global %s ## Warning: Unused global\n" % g)
  1774. if deparsed.ast_errors:
  1775. deparsed.write("# NOTE: have decompilation errors.\n")
  1776. deparsed.write("# Use -t option to show full context.")
  1777. for err in deparsed.ast_errors:
  1778. deparsed.write(err)
  1779. deparsed.ERROR = True
  1780. if deparsed.ERROR:
  1781. raise deparsed.ERROR
  1782. # To keep the API consistent with previous releases, convert
  1783. # deparse.offset values into NodeInfo items
  1784. for tup, node in deparsed.offsets.items():
  1785. deparsed.offsets[tup] = NodeInfo(
  1786. node=node, start=node.start, finish=node.finish
  1787. )
  1788. deparsed.scanner = scanner
  1789. return deparsed
  1790. def find_gt(a, x):
  1791. "Find leftmost value greater than x"
  1792. i = bisect_right(a, x)
  1793. if i != len(a):
  1794. return a[i]
  1795. raise ValueError
  1796. def code_deparse_around_offset(
  1797. name,
  1798. offset,
  1799. co,
  1800. out=StringIO(),
  1801. version: Optional[tuple] = None,
  1802. is_pypy: bool = False,
  1803. debug_opts=DEFAULT_DEBUG_OPTS,
  1804. ):
  1805. """
  1806. Like deparse_code(), but given a function/module name and
  1807. offset, finds the node closest to offset. If offset is not an instruction boundary,
  1808. we raise an IndexError.
  1809. """
  1810. assert iscode(co)
  1811. if version is None:
  1812. version = PYTHON_VERSION_TRIPLE
  1813. if is_pypy is None:
  1814. is_pypy = IS_PYPY
  1815. deparsed = code_deparse(co, out, version, is_pypy, debug_opts)
  1816. if (name, offset) in deparsed.offsets.keys():
  1817. # This is the easy case
  1818. return deparsed
  1819. valid_offsets = [t for t in deparsed.offsets if isinstance(t[1], int)]
  1820. offset_list = sorted([t[1] for t in valid_offsets if t[0] == name])
  1821. # FIXME: should check for branching?
  1822. found_offset = find_gt(offset_list, offset)
  1823. deparsed.offsets[name, offset] = deparsed.offsets[name, found_offset]
  1824. return deparsed
  1825. # Deprecated. Here still for compatibility
  1826. def deparse_code_around_offset(
  1827. name,
  1828. offset,
  1829. version,
  1830. co,
  1831. out=StringIO(),
  1832. showasm=False,
  1833. showast=False,
  1834. showgrammar=PARSER_DEFAULT_DEBUG,
  1835. is_pypy=False,
  1836. ):
  1837. debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
  1838. return code_deparse(name, offset, co, out, version, is_pypy, debug_opts)
  1839. def op_at_code_loc(code, loc, opc):
  1840. """Return the instruction name at code[loc] using
  1841. opc to look up instruction names. Returns 'got IndexError'
  1842. if code[loc] is invalid.
  1843. `code` is instruction bytecode, `loc` is an offset (integer) and
  1844. `opc` is an opcode module from `xdis`.
  1845. """
  1846. try:
  1847. op = code[loc]
  1848. except IndexError:
  1849. return "got IndexError"
  1850. return opc.opname[op]
  1851. def deparsed_find(tup, deparsed, code):
  1852. """Return a NodeInfo nametuple for a fragment-deparsed `deparsed` at `tup`.
  1853. `tup` is a name and offset tuple, `deparsed` is a fragment object
  1854. and `code` is instruction bytecode."""
  1855. nodeInfo = None
  1856. name, last_i = tup
  1857. if not hasattr(deparsed, "offsets"):
  1858. return None
  1859. if (name, last_i) in deparsed.offsets.keys():
  1860. nodeInfo = deparsed.offsets[name, last_i]
  1861. else:
  1862. from decompyle3.scanner import get_scanner
  1863. scanner = get_scanner(deparsed.version)
  1864. co = code.co_code
  1865. if op_at_code_loc(co, last_i, scanner.opc) == "DUP_TOP":
  1866. offset = deparsed.scanner.next_offset(co[last_i], last_i)
  1867. if (name, offset) in deparsed.offsets:
  1868. nodeInfo = deparsed.offsets[name, offset]
  1869. return nodeInfo
  1870. # if __name__ == "__main__":
  1871. # def deparse_test(co, is_pypy=IS_PYPY):
  1872. # deparsed = code_deparse(co, is_pypy=IS_PYPY)
  1873. # print("deparsed source")
  1874. # print(deparsed.text, "\n")
  1875. # print("------------------------")
  1876. # for name, offset in sorted(deparsed.offsets.keys(), key=lambda x: str(x[0])):
  1877. # print("name %s, offset %s" % (name, offset))
  1878. # nodeInfo = deparsed.offsets[name, offset]
  1879. # nodeInfo2 = deparsed_find((name, offset), deparsed, co)
  1880. # assert nodeInfo == nodeInfo2
  1881. # node = nodeInfo.node
  1882. # extractInfo = deparsed.extract_node_info(node)
  1883. # print("code: %s" % node.kind)
  1884. # # print extractInfo
  1885. # print(extractInfo.selectedText)
  1886. # print(extractInfo.selectedLine)
  1887. # print(extractInfo.markerLine)
  1888. # extractInfo, p = deparsed.extract_parent_info(node)
  1889. # if extractInfo:
  1890. # print("Contained in...")
  1891. # print(extractInfo.selectedLine)
  1892. # print(extractInfo.markerLine)
  1893. # print("code: %s" % p.kind)
  1894. # print("=" * 40)
  1895. # pass
  1896. # pass
  1897. # return
  1898. # def deparse_test_around(offset, name, co, is_pypy=IS_PYPY):
  1899. # deparsed = code_deparse_around_offset(name, offset, co)
  1900. # print("deparsed source")
  1901. # print(deparsed.text, "\n")
  1902. # print("------------------------")
  1903. # for name, offset in sorted(deparsed.offsets.keys(), key=lambda x: str(x[0])):
  1904. # print("name %s, offset %s" % (name, offset))
  1905. # nodeInfo = deparsed.offsets[name, offset]
  1906. # node = nodeInfo.node
  1907. # extractInfo = deparsed.extract_node_info(node)
  1908. # print("code: %s" % node.kind)
  1909. # # print extractInfo
  1910. # print(extractInfo.selectedText)
  1911. # print(extractInfo.selectedLine)
  1912. # print(extractInfo.markerLine)
  1913. # extractInfo, p = deparsed.extract_parent_info(node)
  1914. # if extractInfo:
  1915. # print("Contained in...")
  1916. # print(extractInfo.selectedLine)
  1917. # print(extractInfo.markerLine)
  1918. # print("code: %s" % p.kind)
  1919. # print("=" * 40)
  1920. # pass
  1921. # pass
  1922. # return
  1923. # def get_code_for_fn(fn):
  1924. # return fn.__code__
  1925. # def test():
  1926. # import os, sys
  1927. # def get_dups(li: list) -> set:
  1928. # dups = {}
  1929. # for item in li:
  1930. # dups[item] = dups.get(item, -1) + 1
  1931. # # return dups
  1932. # return {k for k in dups.keys() if dups[k] > 0}
  1933. # def div_test(a, b, c):
  1934. # return a / b / c
  1935. # def gcd(a, b):
  1936. # if a > b:
  1937. # (a, b) = (b, a)
  1938. # pass
  1939. # if a <= 0:
  1940. # return None
  1941. # if a == 1 or a == b:
  1942. # return a
  1943. # return gcd(b - a, a)
  1944. # # check_args(['3', '5'])
  1945. # # deparse_test(get_code_for_fn(gcd))
  1946. # deparse_test(get_code_for_fn(get_dups))
  1947. # # deparse_test(get_code_for_fn(test))
  1948. # # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
  1949. # # deparse_test(get_code_for_fn(FragmentsWalker.n_list))
  1950. # print("=" * 30)
  1951. # # deparse_test_around(408, 'n_list',
  1952. # get_code_for_fn(FragmentsWalker.n_build_list))
  1953. # # deparse_test(inspect.currentframe().f_code)