unmarshal.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. # Copyright (c) 2015-2021, 2024-2025 by Rocky Bernstein
  2. # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. """CPython magic- and version-independent Python object
  18. deserialization (unmarshal).
  19. This is needed when the bytecode extracted is from
  20. a different version than the currently-running Python.
  21. When the running interpreter and the read-in bytecode are the same,
  22. you can use Python's built-in ``marshal.loads()`` to produce a code
  23. object.
  24. """
  25. import io
  26. import sys
  27. from struct import unpack
  28. from types import EllipsisType
  29. from typing import Union
  30. from xdis.codetype import to_portable
  31. from xdis.cross_types import LongTypeForPython3, UnicodeForPython3
  32. from xdis.magics import GRAAL3_MAGICS, PYPY3_MAGICS, RUSTPYTHON_MAGICS, magic_int2tuple
  33. from xdis.version_info import PYTHON3, PYTHON_VERSION_TRIPLE, version_tuple_to_str
  34. if PYTHON3:
  35. def long(n: int) -> LongTypeForPython3:
  36. return LongTypeForPython3(n)
  37. else:
  38. import unicodedata
  39. # FIXME: we should write a bytes() class with a repr
  40. # that prints the b'' prefix so that Python2 can
  41. # print out Python3 code correctly
  42. # Bit set on marshalType if we should
  43. # add obj to internObjects.
  44. # FLAG_REF is the marshal.c name
  45. FLAG_REF = 0x80
  46. # The keys in the following dictionary are unmarshal codes, like "s",
  47. # "c", "<", etc. The values of the dictionary are names of routines
  48. # to call that do the data unmarshaling.
  49. #
  50. # Note: we could eliminate the parameters if this were all inside a
  51. # class. This might be good from an efficiency standpoint, and bad
  52. # from a functional-programming standpoint. Pick your poison.
  53. # EDIT: I'm choosing efficiency over functional programming.
  54. UNMARSHAL_DISPATCH_TABLE = {
  55. "0": "C_NULL",
  56. "N": "None",
  57. "S": "stopIteration",
  58. ".": "Ellipsis",
  59. "F": "False",
  60. "T": "True",
  61. "i": "int32",
  62. "l": "long",
  63. "I": "int64",
  64. "f": "float",
  65. "g": "binary_float",
  66. "x": "complex",
  67. "y": "binary_complex",
  68. "s": "string",
  69. "A": "ASCII_interned",
  70. "a": "ASCII",
  71. "z": "short_ASCII",
  72. "Z": "short_ASCII_interned",
  73. "t": "interned",
  74. "u": "unicode",
  75. ")": "small_tuple",
  76. "(": "tuple",
  77. "[": "list",
  78. "<": "frozenset",
  79. ">": "set",
  80. "{": "dict",
  81. "R": "python2_string_reference",
  82. "c": "code",
  83. "C": "code", # Older Python code
  84. "r": "object_reference",
  85. "?": "unknown",
  86. }
  87. def compat_str(s: Union[str, bytes]) -> Union[str, bytes]:
  88. """
  89. This handles working with strings between Python2 and Python3.
  90. """
  91. if isinstance(s, bytes):
  92. try:
  93. return s.decode("utf-8")
  94. except UnicodeDecodeError:
  95. # If not Unicode, return bytes,
  96. # and it will get converted to str when needed.
  97. return s
  98. elif not isinstance(s, str):
  99. return str(s)
  100. else:
  101. return s
  102. def compat_u2s(u) -> bytes | str:
  103. if PYTHON_VERSION_TRIPLE < (3, 0):
  104. # See also ``unaccent.py`` which can be found using Google. I
  105. # found it and this code via
  106. # https://www.peterbe.com/plog/unicode-to-ascii where it is a
  107. # dead link. That can potentially do a better job in converting accents.
  108. s = unicodedata.normalize("NFKD", u)
  109. try:
  110. return s.encode("ascii")
  111. except UnicodeEncodeError:
  112. return s
  113. else:
  114. return str(u)
  115. class _VersionIndependentUnmarshaller:
  116. def __init__(self, fp, magic_int, bytes_for_s, code_objects={}) -> None:
  117. """
  118. Marshal versions:
  119. 0/Historical: Until 2.4/magic int 62041
  120. 1: [2.4, 2.5) (self.magic_int: 62041 until 62071)
  121. 2: [2.5, 3.4a0) (self.magic_int: 62071 until 3250)
  122. 3: [3.4a0, 3.4a3) (self.magic_int: 3250 until 3280)
  123. 4: [3.4a3, current) (self.magic_int: 3280 onwards)
  124. In Python 3, a ``bytes`` type is used for strings.
  125. """
  126. self.fp = fp
  127. self.magic_int = magic_int
  128. self.code_objects = code_objects
  129. self.bytes_for_s = bytes_for_s
  130. version = magic_int2tuple(self.magic_int)
  131. if version >= (3, 4):
  132. if self.magic_int in (3250, 3260, 3270):
  133. self.marshal_version = 3
  134. else:
  135. self.marshal_version = 4
  136. elif (3, 4) > version >= (2, 5):
  137. self.marshal_version = 2
  138. elif (2, 5) > version >= (2, 4):
  139. self.marshal_version = 1
  140. else:
  141. self.marshal_version = 0
  142. self.internStrings = []
  143. self.internObjects = []
  144. self.version_tuple = tuple()
  145. self.is_graal = magic_int in GRAAL3_MAGICS
  146. self.is_pypy = magic_int in PYPY3_MAGICS
  147. self.is_rust = magic_int in RUSTPYTHON_MAGICS
  148. if magic_int in RUSTPYTHON_MAGICS:
  149. raise NotImplementedError(
  150. f"RustPython {version_tuple_to_str(version)} is not supported yet."
  151. )
  152. def load(self):
  153. """
  154. ``marshal.load()`` written in Python. When the Python bytecode magic loaded is the
  155. same magic for the running Python interpreter, we can simply use the
  156. Python-supplied marshal.load().
  157. However, we need to use this when versions are different since the internal
  158. code structures are different. Sigh.
  159. """
  160. if self.marshal_version == 0:
  161. self.internStrings = []
  162. if self.marshal_version < 3:
  163. assert self.internObjects == []
  164. return self.r_object()
  165. # Python 3.4+ support for reference objects.
  166. # The names follow marshal.c
  167. def r_ref_reserve(self, obj, save_ref):
  168. i = None
  169. if save_ref:
  170. i = len(self.internObjects)
  171. self.internObjects.append(obj)
  172. return obj, i
  173. def r_ref_insert(self, obj, i: int | None):
  174. if i is not None:
  175. self.internObjects[i] = obj
  176. return obj
  177. def r_ref(self, obj, save_ref):
  178. if save_ref:
  179. self.internObjects.append(obj)
  180. return obj
  181. # In marshal.c this is one big case statement
  182. def r_object(self, bytes_for_s: bool = False):
  183. """
  184. In Python3 strings are bytes type
  185. """
  186. byte1 = ord(self.fp.read(1))
  187. # FLAG_REF indicates whether we "intern" or
  188. # save a reference to the object.
  189. # byte1 without that reference is the
  190. # marshal type code, an ASCII character.
  191. save_ref = False
  192. if byte1 & FLAG_REF:
  193. # Since 3.4, "flag" is the marshal.c name
  194. save_ref = True
  195. byte1 = byte1 & (FLAG_REF - 1)
  196. marshal_type = chr(byte1)
  197. # print(marshal_type) # debug
  198. if marshal_type in UNMARSHAL_DISPATCH_TABLE:
  199. func_suffix = UNMARSHAL_DISPATCH_TABLE[marshal_type]
  200. unmarshal_func = getattr(self, "t_" + func_suffix)
  201. return unmarshal_func(save_ref, bytes_for_s)
  202. else:
  203. try:
  204. sys.stderr.write(
  205. "Unknown type %i (hex %x) %c\n"
  206. % (ord(marshal_type), ord(marshal_type), marshal_type)
  207. )
  208. except TypeError:
  209. sys.stderr.write(
  210. "Unknown type %i %c\n" % (ord(marshal_type), marshal_type)
  211. )
  212. return
  213. # In C this NULL. Not sure what it should
  214. # translate here. Note NULL != None which is below
  215. def t_C_NULL(self, save_ref, bytes_for_s: bool = False) -> None:
  216. return None
  217. def t_None(self, save_ref, bytes_for_s: bool = False) -> None:
  218. return None
  219. def t_stopIteration(
  220. self, save_ref, bytes_for_s: bool = False
  221. ) -> type[StopIteration]:
  222. return StopIteration
  223. def t_Ellipsis(self, save_ref, bytes_for_s: bool = False) -> EllipsisType:
  224. return Ellipsis
  225. def t_False(self, save_ref, bytes_for_s: bool = False) -> bool:
  226. return False
  227. def t_True(self, save_ref, bytes_for_s: bool = False) -> bool:
  228. return True
  229. def t_int32(self, save_ref, bytes_for_s: bool = False):
  230. return self.r_ref(int(unpack("<i", self.fp.read(4))[0]), save_ref)
  231. def t_long(self, save_ref, bytes_for_s: bool = False):
  232. n = unpack("<i", self.fp.read(4))[0]
  233. if n == 0:
  234. return long(0)
  235. size = abs(n)
  236. d = long(0)
  237. for j in range(0, size):
  238. md = int(unpack("<h", self.fp.read(2))[0])
  239. # This operation and turn "d" from a long back
  240. # into an int.
  241. d += md << j * 15
  242. d = long(d)
  243. if n < 0:
  244. d = long(d * -1)
  245. return self.r_ref(d, save_ref)
  246. # Python 3.4 removed this.
  247. def t_int64(self, save_ref, bytes_for_s: bool = False):
  248. obj = unpack("<q", self.fp.read(8))[0]
  249. if save_ref:
  250. self.internObjects.append(obj)
  251. return obj
  252. # float - Seems not in use after Python 2.4
  253. def t_float(self, save_ref, bytes_for_s: bool = False):
  254. strsize = unpack("B", self.fp.read(1))[0]
  255. s = self.fp.read(strsize)
  256. return self.r_ref(float(s), save_ref)
  257. def t_binary_float(self, save_ref, bytes_for_s: bool = False):
  258. return self.r_ref(float(unpack("<d", self.fp.read(8))[0]), save_ref)
  259. def t_complex(self, save_ref, bytes_for_s: bool = False):
  260. def unpack_pre_24() -> float:
  261. return float(self.fp.read(unpack("B", self.fp.read(1))[0]))
  262. def unpack_newer() -> float:
  263. return float(self.fp.read(unpack("<i", self.fp.read(4))[0]))
  264. get_float = unpack_pre_24 if self.magic_int <= 62061 else unpack_newer
  265. real = get_float()
  266. imag = get_float()
  267. return self.r_ref(complex(real, imag), save_ref)
  268. def t_binary_complex(self, save_ref, bytes_for_s: bool = False):
  269. # binary complex
  270. real = unpack("<d", self.fp.read(8))[0]
  271. imag = unpack("<d", self.fp.read(8))[0]
  272. return self.r_ref(complex(real, imag), save_ref)
  273. # Note: could mean bytes in Python3 processing Python2 bytecode
  274. def t_string(self, save_ref, bytes_for_s: bool):
  275. """
  276. In Python3, this is a ``bytes`` type. In Python2, it is a string type;
  277. ``bytes_for_s`` distinguishes what we need.
  278. """
  279. strsize = unpack("<i", self.fp.read(4))[0]
  280. s = self.fp.read(strsize)
  281. if not bytes_for_s:
  282. s = compat_str(s)
  283. return self.r_ref(s, save_ref)
  284. # Python 3.4
  285. def t_ASCII_interned(self, save_ref, bytes_for_s: bool = False):
  286. """
  287. There are true strings in Python3 as opposed to
  288. bytes. "interned" just means we keep a reference to
  289. the string.
  290. """
  291. # FIXME: check
  292. strsize = unpack("<i", self.fp.read(4))[0]
  293. interned = compat_str(self.fp.read(strsize))
  294. self.internStrings.append(interned)
  295. return self.r_ref(interned, save_ref)
  296. # Since Python 3.4
  297. def t_ASCII(self, save_ref, bytes_for_s: bool = False):
  298. """
  299. There are true strings in Python3 as opposed to
  300. bytes.
  301. """
  302. strsize = unpack("<i", self.fp.read(4))[0]
  303. s = self.fp.read(strsize)
  304. s = compat_str(s)
  305. return self.r_ref(s, save_ref)
  306. # Since Python 3.4
  307. def t_short_ASCII(self, save_ref, bytes_for_s: bool = False):
  308. strsize = unpack("B", self.fp.read(1))[0]
  309. return self.r_ref(compat_str(self.fp.read(strsize)), save_ref)
  310. # Since Python 3.4
  311. def t_short_ASCII_interned(self, save_ref, bytes_for_s: bool = False):
  312. # FIXME: check
  313. strsize = unpack("B", self.fp.read(1))[0]
  314. interned = compat_str(self.fp.read(strsize))
  315. self.internStrings.append(interned)
  316. return self.r_ref(interned, save_ref)
  317. # Since Python 3.4
  318. def t_interned(self, save_ref, bytes_for_s: bool = False):
  319. strsize = unpack("<i", self.fp.read(4))[0]
  320. interned = compat_str(self.fp.read(strsize))
  321. self.internStrings.append(interned)
  322. return self.r_ref(interned, save_ref)
  323. def t_unicode(self, save_ref, bytes_for_s: bool = False):
  324. strsize = unpack("<i", self.fp.read(4))[0]
  325. unicodestring = self.fp.read(strsize)
  326. if PYTHON_VERSION_TRIPLE >= (3, 0) and self.version_tuple < (3, 0):
  327. string = UnicodeForPython3(unicodestring)
  328. else:
  329. string = unicodestring.decode()
  330. return self.r_ref(string, save_ref)
  331. # Since Python 3.4
  332. def t_small_tuple(self, save_ref, bytes_for_s: bool = False):
  333. # small tuple - since Python 3.4
  334. tuplesize = unpack("B", self.fp.read(1))[0]
  335. ret, i = self.r_ref_reserve(tuple(), save_ref)
  336. while tuplesize > 0:
  337. ret += (self.r_object(bytes_for_s=bytes_for_s),)
  338. tuplesize -= 1
  339. pass
  340. return self.r_ref_insert(ret, i)
  341. def t_tuple(self, save_ref, bytes_for_s: bool = False):
  342. tuplesize = unpack("<i", self.fp.read(4))[0]
  343. ret = self.r_ref(tuple(), save_ref)
  344. while tuplesize > 0:
  345. ret += (self.r_object(bytes_for_s=bytes_for_s),)
  346. tuplesize -= 1
  347. return ret
  348. def t_list(self, save_ref, bytes_for_s: bool = False):
  349. # FIXME: check me
  350. n = unpack("<i", self.fp.read(4))[0]
  351. ret = self.r_ref(list(), save_ref)
  352. while n > 0:
  353. ret += (self.r_object(bytes_for_s=bytes_for_s),)
  354. n -= 1
  355. return ret
  356. def t_frozenset(self, save_ref, bytes_for_s: bool = False):
  357. setsize = unpack("<i", self.fp.read(4))[0]
  358. ret, i = self.r_ref_reserve(tuple(), save_ref)
  359. while setsize > 0:
  360. ret += (self.r_object(bytes_for_s=bytes_for_s),)
  361. setsize -= 1
  362. return self.r_ref_insert(frozenset(ret), i)
  363. def t_set(self, save_ref, bytes_for_s: bool = False):
  364. setsize = unpack("<i", self.fp.read(4))[0]
  365. ret, i = self.r_ref_reserve(tuple(), save_ref)
  366. while setsize > 0:
  367. ret += (self.r_object(bytes_for_s=bytes_for_s),)
  368. setsize -= 1
  369. return self.r_ref_insert(set(ret), i)
  370. def t_dict(self, save_ref, bytes_for_s: bool = False):
  371. ret = self.r_ref(dict(), save_ref)
  372. # dictionary
  373. while True:
  374. key = self.r_object(bytes_for_s=bytes_for_s)
  375. if key is None:
  376. break
  377. val = self.r_object(bytes_for_s=bytes_for_s)
  378. if val is None:
  379. break
  380. ret[key] = val
  381. pass
  382. return ret
  383. def t_python2_string_reference(self, save_ref, bytes_for_s: bool = False):
  384. refnum = unpack("<i", self.fp.read(4))[0]
  385. return self.internStrings[refnum]
  386. def t_code(self, save_ref, bytes_for_s: bool = False):
  387. """
  388. Python code type in all of its horrific variations.
  389. """
  390. # FIXME: use tables to simplify this?
  391. # FIXME: Python 1.0 .. 1.3 isn't well known
  392. ret, i = self.r_ref_reserve(None, save_ref)
  393. self.version_tuple = magic_int2tuple(self.magic_int)
  394. if self.version_tuple >= (2, 3):
  395. co_argcount = unpack("<i", self.fp.read(4))[0]
  396. elif self.version_tuple >= (1, 3):
  397. co_argcount = unpack("<h", self.fp.read(2))[0]
  398. else:
  399. co_argcount = 0
  400. if self.version_tuple >= (3, 8):
  401. co_posonlyargcount = (
  402. 0
  403. if self.magic_int in (3400, 3401, 3410, 3411)
  404. else unpack("<i", self.fp.read(4))[0]
  405. )
  406. else:
  407. co_posonlyargcount = None
  408. if self.version_tuple >= (3, 0):
  409. kwonlyargcount = unpack("<i", self.fp.read(4))[0]
  410. else:
  411. kwonlyargcount = 0
  412. co_nlocals = 0
  413. if self.version_tuple < (3, 11) or (
  414. self.version_tuple[:2] == (3, 11) and self.is_pypy
  415. ):
  416. if self.version_tuple >= (2, 3):
  417. co_nlocals = unpack("<i", self.fp.read(4))[0]
  418. elif self.version_tuple >= (1, 3):
  419. co_nlocals = unpack("<h", self.fp.read(2))[0]
  420. if self.version_tuple >= (2, 3):
  421. co_stacksize = unpack("<i", self.fp.read(4))[0]
  422. elif self.version_tuple >= (1, 5):
  423. co_stacksize = unpack("<h", self.fp.read(2))[0]
  424. else:
  425. co_stacksize = 0
  426. if self.version_tuple >= (2, 3):
  427. co_flags = unpack("<i", self.fp.read(4))[0]
  428. elif self.version_tuple >= (1, 3):
  429. co_flags = unpack("<h", self.fp.read(2))[0]
  430. else:
  431. co_flags = 0
  432. co_code = self.r_object(bytes_for_s=True)
  433. # FIXME: Check/verify that is true:
  434. bytes_for_s = PYTHON_VERSION_TRIPLE >= (3, 0) and (self.version_tuple > (3, 0))
  435. if self.is_graal:
  436. co_consts = tuple()
  437. co_names = tuple()
  438. code = to_portable(
  439. co_argcount=0,
  440. co_posonlyargcount=0,
  441. co_kwonlyargcount=0,
  442. co_nlocals=0,
  443. co_stacksize=0,
  444. co_flags=0,
  445. co_code=co_code,
  446. co_consts=tuple(),
  447. co_names=tuple(),
  448. co_varnames=tuple(),
  449. co_filename="??",
  450. co_name="??",
  451. co_qualname="??",
  452. co_firstlineno=0,
  453. co_lnotab="",
  454. co_freevars=tuple(),
  455. co_cellvars=tuple(),
  456. co_exceptiontable=None,
  457. version_triple=self.version_tuple,
  458. )
  459. ret = code
  460. return self.r_ref_insert(ret, i)
  461. co_consts = self.r_object(bytes_for_s=bytes_for_s)
  462. co_names = self.r_object(bytes_for_s=bytes_for_s)
  463. co_varnames = tuple()
  464. co_freevars = tuple()
  465. co_cellvars = tuple()
  466. if self.version_tuple >= (3, 11) and not self.is_pypy:
  467. # parse localsplusnames list: https://github.com/python/cpython/blob/3.11/Objects/codeobject.c#L208C12
  468. co_localsplusnames = self.r_object(bytes_for_s=bytes_for_s)
  469. co_localspluskinds = self.r_object(bytes_for_s=bytes_for_s)
  470. CO_FAST_LOCAL = 0x20
  471. CO_FAST_CELL = 0x40
  472. CO_FAST_FREE = 0x80
  473. for name, kind in zip(co_localsplusnames, co_localspluskinds):
  474. if kind & CO_FAST_LOCAL:
  475. co_varnames += (name,)
  476. if kind & CO_FAST_CELL:
  477. co_cellvars += (name,)
  478. elif kind & CO_FAST_CELL:
  479. co_cellvars += (name,)
  480. elif kind & CO_FAST_FREE:
  481. co_freevars += (name,)
  482. co_nlocals = len(co_varnames)
  483. co_filename = self.r_object(bytes_for_s=bytes_for_s)
  484. co_name = self.r_object(bytes_for_s=bytes_for_s)
  485. co_qualname = self.r_object(bytes_for_s=bytes_for_s)
  486. pass
  487. else:
  488. co_qualname = None
  489. if self.version_tuple >= (1, 3):
  490. co_varnames = self.r_object(bytes_for_s=False)
  491. else:
  492. co_varnames = tuple()
  493. if self.version_tuple >= (2, 0):
  494. co_freevars = self.r_object(bytes_for_s=bytes_for_s)
  495. co_cellvars = self.r_object(bytes_for_s=bytes_for_s)
  496. co_filename = self.r_object(bytes_for_s=bytes_for_s)
  497. co_name = self.r_object(bytes_for_s=bytes_for_s)
  498. if self.version_tuple >= (3, 11) and self.is_pypy:
  499. co_qualname = self.r_object(bytes_for_s=bytes_for_s)
  500. co_exceptiontable = None
  501. if self.version_tuple >= (1, 5):
  502. if self.version_tuple >= (2, 3):
  503. co_firstlineno = unpack("<i", self.fp.read(4))[0]
  504. else:
  505. co_firstlineno = unpack("<h", self.fp.read(2))[0]
  506. if self.version_tuple >= (3, 11) and not self.is_pypy:
  507. co_linetable = self.r_object(bytes_for_s=bytes_for_s)
  508. co_lnotab = (
  509. co_linetable # will be parsed later in opcode.findlinestarts
  510. )
  511. co_exceptiontable = self.r_object(bytes_for_s=bytes_for_s)
  512. else:
  513. co_lnotab = self.r_object(bytes_for_s=bytes_for_s)
  514. else:
  515. # < 1.5 there is no lnotab, so no firstlineno.
  516. # SET_LINENO is used instead.
  517. co_firstlineno = -1 # Bogus sentinel value
  518. co_lnotab = b""
  519. code = to_portable(
  520. co_argcount=co_argcount,
  521. co_posonlyargcount=co_posonlyargcount,
  522. co_kwonlyargcount=kwonlyargcount,
  523. co_nlocals=co_nlocals,
  524. co_stacksize=co_stacksize,
  525. co_flags=co_flags,
  526. co_code=co_code,
  527. co_consts=co_consts,
  528. co_names=co_names,
  529. co_varnames=co_varnames,
  530. co_filename=co_filename,
  531. co_name=co_name,
  532. co_qualname=co_qualname,
  533. co_firstlineno=co_firstlineno,
  534. co_lnotab=co_lnotab,
  535. co_freevars=co_freevars,
  536. co_cellvars=co_cellvars,
  537. co_exceptiontable=co_exceptiontable,
  538. version_triple=self.version_tuple,
  539. )
  540. self.code_objects[str(code)] = code
  541. ret = code
  542. return self.r_ref_insert(ret, i)
  543. # Since Python 3.4
  544. def t_object_reference(self, save_ref=None, bytes_for_s: bool = False):
  545. refnum = unpack("<i", self.fp.read(4))[0]
  546. o = self.internObjects[refnum]
  547. return o
  548. def t_unknown(self, save_ref=None, bytes_for_s: bool = False):
  549. raise KeyError("?")
  550. # _________________________________________________________________
  551. #
  552. # user interface
  553. def load_code(fp, magic_int, bytes_for_s: bool = False, code_objects={}):
  554. if isinstance(fp, bytes):
  555. fp = io.BytesIO(fp)
  556. um_gen = _VersionIndependentUnmarshaller(
  557. fp, magic_int, bytes_for_s, code_objects=code_objects
  558. )
  559. return um_gen.load()